পরিষেবা থেকে অ্যান্ড্রয়েড আপডেট ক্রিয়াকলাপ UI


102

আমার একটি পরিষেবা রয়েছে যা সর্বদা নতুন কাজের জন্য যাচাই করে চলেছে। যদি নতুন কোনও কাজ হয় তবে আমি তথ্যটি দেখানোর জন্য ক্রিয়াকলাপ UI রিফ্রেশ করতে চাই। আমি এই উদাহরণটি https://github.com/commonsguy/cw-andtutorials/tree/master/18- স্থানীয় সার্ভিস / পেয়েছি । এটা কি ভাল পন্থা? অন্য কোন উদাহরণ?

ধন্যবাদ।


আমার উত্তর এখানে দেখুন। শ্রোতার সাহায্যে শ্রেণীর মধ্যে যোগাযোগের জন্য একটি ইন্টারফেস সংজ্ঞায়িত করা সহজ। stackoverflow.com/questions/14660671/…
সাইমন

উত্তর:


228

আমার আসল উত্তরের জন্য নীচে দেখুন - সেই প্যাটার্নটি ভাল কাজ করেছে তবে সম্প্রতি আমি পরিষেবা / ক্রিয়াকলাপের যোগাযোগের জন্য আলাদা পদ্ধতির ব্যবহার শুরু করেছি:

  • একটি সীমাবদ্ধ পরিষেবা ব্যবহার করুন যা ক্রিয়াকলাপটিকে পরিষেবাতে সরাসরি রেফারেন্স পেতে সক্ষম করে, সুতরাং এজেন্টগুলি ব্যবহার না করে সরাসরি কলগুলিকে অনুমতি দেয়।
  • অ্যাসিক্রোনাস অপারেশনগুলি সম্পাদন করতে আরএক্সজাভা ব্যবহার করুন।

  • যদি কোনও ক্রিয়াকলাপ না চলাকালীনও যদি পরিষেবার জন্য ব্যাকগ্রাউন্ড ক্রিয়াকলাপ চালিয়ে যাওয়া প্রয়োজন হয় তবে অ্যাপ্লিকেশন শ্রেণি থেকে পরিষেবাটিও শুরু করুন যাতে আনবাউন্ড হওয়া বন্ধ না হয়।

স্টার্ট সার্ভিস () / লোকালব্রডকাস্ট কৌশলটির তুলনায় এই পদ্ধতির মধ্যে আমি যে সুবিধাগুলি পেয়েছি তা হ'ল

  • পার্সেবল বাস্তবায়নের জন্য ডেটা অবজেক্টগুলির প্রয়োজন নেই - এটি আমার পক্ষে বিশেষভাবে গুরুত্বপূর্ণ কারণ আমি এখন অ্যান্ড্রয়েড এবং আইওএসের মধ্যে কোড ভাগ করছি (রোবওএমএম ব্যবহার করে)
  • আরএক্সজাভা ক্যানড (এবং ক্রস-প্ল্যাটফর্ম) শিডিয়ুলিং এবং ক্রমানুসারে অ্যাসিঙ্ক্রোনাস অপারেশনগুলির সহজ সংমিশ্রণ সরবরাহ করে।
  • লোকালব্রোডকাস্ট ব্যবহারের চেয়ে এটি আরও দক্ষ হওয়া উচিত, যদিও আরএক্সবাভা ব্যবহারের ওভারহেড এটি ছাড়িয়ে যেতে পারে।

কিছু উদাহরণ কোড। প্রথম পরিষেবা:

public class AndroidBmService extends Service implements BmService {

    private static final int PRESSURE_RATE = 500000;   // microseconds between pressure updates
    private SensorManager sensorManager;
    private SensorEventListener pressureListener;
    private ObservableEmitter<Float> pressureObserver;
    private Observable<Float> pressureObservable;

    public class LocalBinder extends Binder {
        public AndroidBmService getService() {
            return AndroidBmService.this;
        }
    }

    private IBinder binder = new LocalBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        logMsg("Service bound");
        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
        Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
        if(pressureSensor != null)
            sensorManager.registerListener(pressureListener = new SensorEventListener() {
                @Override
                public void onSensorChanged(SensorEvent event) {
                    if(pressureObserver != null) {
                        float lastPressure = event.values[0];
                        float lastPressureAltitude = (float)((1 - Math.pow(lastPressure / 1013.25, 0.190284)) * 145366.45);
                        pressureObserver.onNext(lastPressureAltitude);
                    }
                }

                @Override
                public void onAccuracyChanged(Sensor sensor, int accuracy) {

                }
            }, pressureSensor, PRESSURE_RATE);
    }

    @Override
    public Observable<Float> observePressure() {
        if(pressureObservable == null) {
            pressureObservable = Observable.create(emitter -> pressureObserver = emitter);
            pressureObservable = pressureObservable.share();
        }
         return pressureObservable;
    }

    @Override
    public void onDestroy() {
        if(pressureListener != null)
            sensorManager.unregisterListener(pressureListener);
    }
} 

এবং একটি ক্রিয়াকলাপ যা পরিষেবার সাথে আবদ্ধ হয় এবং চাপের উচ্চতার আপডেটগুলি গ্রহণ করে:

public class TestActivity extends AppCompatActivity {

    private ContentTestBinding binding;
    private ServiceConnection serviceConnection;
    private AndroidBmService service;
    private Disposable disposable;

    @Override
    protected void onDestroy() {
        if(disposable != null)
            disposable.dispose();
        unbindService(serviceConnection);
        super.onDestroy();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.content_test);
        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                logMsg("BlueMAX service bound");
                service = ((AndroidBmService.LocalBinder)iBinder).getService();
                disposable = service.observePressure()
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(altitude ->
                        binding.altitude.setText(
                            String.format(Locale.US,
                                "Pressure Altitude %d feet",
                                altitude.intValue())));
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                logMsg("Service disconnected");
            }
        };
        bindService(new Intent(
            this, AndroidBmService.class),
            serviceConnection, BIND_AUTO_CREATE);
    }
}

এই ক্রিয়াকলাপের জন্য বিন্যাসটি হ'ল:

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.controlj.mfgtest.TestActivity">

        <TextView
            tools:text="Pressure"
            android:id="@+id/altitude"
            android:gravity="center_horizontal"
            android:layout_gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </LinearLayout>
</layout>

সেবা চাহিদা একটি বাউন্ড ভ্রমণ ছাড়া পটভূমি চালানোর জন্য যদি পাশাপাশি অ্যাপ্লিকেশন ক্লাস থেকে শুরু করা যাবে OnCreate()ব্যবহার Context#startService()


আমার আসল উত্তর (২০১৩ থেকে):

আপনার পরিষেবা: (নীচের উদাহরণ হিসাবে পরিষেবা হিসাবে সিওপিএ ব্যবহার করে)।

লোকালবারডকাস্টম্যানেজার ব্যবহার করুন। আপনার পরিষেবার অনকারেটে, সম্প্রচারক সেট আপ করুন:

broadcaster = LocalBroadcastManager.getInstance(this);

আপনি যখন কোনও কিছুর UI কে অবহিত করতে চান:

static final public String COPA_RESULT = "com.controlj.copame.backend.COPAService.REQUEST_PROCESSED";

static final public String COPA_MESSAGE = "com.controlj.copame.backend.COPAService.COPA_MSG";

public void sendResult(String message) {
    Intent intent = new Intent(COPA_RESULT);
    if(message != null)
        intent.putExtra(COPA_MESSAGE, message);
    broadcaster.sendBroadcast(intent);
}

আপনার ক্রিয়াকলাপে:

অনক্রিটটিতে শ্রোতা তৈরি করুন:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setContentView(R.layout.copa);
    receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String s = intent.getStringExtra(COPAService.COPA_MESSAGE);
            // do something here.
        }
    };
}

এবং এটি অন স্টার্টে নিবন্ধ করুন:

@Override
protected void onStart() {
    super.onStart();
    LocalBroadcastManager.getInstance(this).registerReceiver((receiver), 
        new IntentFilter(COPAService.COPA_RESULT)
    );
}

@Override
protected void onStop() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    super.onStop();
}

4
@ user200658 হ্যাঁ, onStart () এবং onStop () ভ্রমণ জীবনচক্র অংশ - দেখুন ভ্রমণ লাইফ সাইকল
ক্লাইড

8
ক্ষুদ্র মন্তব্য - আপনি COPA_MESSAGE এর সংজ্ঞাটি মিস করছেন।
লাইয়ার

1
আপনাকে ধন্যবাদ :) যারা COPA museT সম্পর্কে জিজ্ঞাসা করছেন, এটি কোনও স্ট্যাটিক ভেরিয়েবলের দ্বারা উত্পাদিত ছাড়া কিছুই নয়। "COPA" হ'ল তার পরিষেবার নাম যাতে আপনি এটি সম্পূর্ণরূপে আপনার সাথে প্রতিস্থাপন করতে পারেন। আমার ক্ষেত্রে এটি স্ট্যাটিক চূড়ান্ত পাবলিক স্ট্রিং এমপি_আরসাল্ট = "কম.ওয়াইফাইড.মিউজিকপ্লেয়ার। মিউজিক সার্ভিস.আরকিউএস_প্রসেসেড";
TheOnlyAnil

1
যদি ব্যবহারকারী ক্রিয়াকলাপ থেকে দূরে নেভিগেট করে এবং পরিষেবাটি শেষ হয়ে যাওয়ার পরে তার কাছে ফিরে আসে তবে ইউআই আপডেট হবে না। এটি পরিচালনা করার সবচেয়ে ভাল উপায় কী?
সেভেজকিং

1
@ সাভেজকিং আপনার onStart()বা onResume()পদ্ধতিতে পরিষেবাটিতে যা কিছু অনুরোধ প্রযোজ্য সেগুলি আপনাকে প্রেরণ করতে হবে । সাধারণভাবে যদি ক্রিয়াকলাপ পরিষেবাটিকে কিছু করতে বলে, তবে ফলাফল পাওয়ার আগে ত্যাগ করে, ফলাফলটি আর প্রয়োজন হয় না তা ধরে নেওয়া যুক্তিসঙ্গত। একইভাবে একটি কার্যকলাপ শুরু করার সময় এটি ধরে নেওয়া উচিত যে পরিষেবা দ্বারা কোনও বকেয়া অনুরোধগুলি প্রক্রিয়াধীন নয়।
ক্লাইড

32

আমার জন্য সহজ সমাধানটি ছিল একটি সম্প্রচার প্রেরণ করা, ক্রিয়াকলাপে আমি রেজিস্ট্রেশন করা এবং সম্প্রচারকে এভাবে সংজ্ঞায়িত করে (আপডেট ইউআরসিভারটি শ্রেণীর উদাহরণ হিসাবে সংজ্ঞায়িত করা হয়):

 IntentFilter filter = new IntentFilter();

 filter.addAction("com.hello.action"); 

 updateUIReciver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                //UI update here

            }
        };
 registerReceiver(updateUIReciver,filter);

এবং পরিষেবাটি থেকে আপনি এই জাতীয় উদ্দেশ্যটি প্রেরণ করুন:

Intent local = new Intent();

local.setAction("com.hello.action");

this.sendBroadcast(local);

ধ্বংস উপর ক্রিয়াকলাপ পুনরুদ্ধার নিবন্ধন করতে ভুলবেন না:

unregisterReceiver(updateUIReciver);

1
এটি আরও ভাল সমাধান তবে লোকালড্রোডকাস্টম্যানেজারটি ব্যবহার করা আরও ভাল হবে যদি এটি প্রয়োগের মধ্যে ব্যবহার করা হয় যা আরও দক্ষ হবে efficient
Psypher

1
এর পরের পদক্ষেপগুলি se সেবায়?
দেবদূত

@ অ্যাঞ্জেল এর পরে আর কোনও পদক্ষেপ নেই, অভিপ্রায়টির অতিরিক্তগুলির মধ্যে কেবল আপনার পছন্দসই ইউআই আপডেট যুক্ত করুন এবং এটি
ইরান কাটসভ

12

আমি এটি করতে সীমাবদ্ধ পরিষেবাটি ব্যবহার করব এবং আমার ক্রিয়াকলাপে শ্রোতার প্রয়োগ করে এর সাথে যোগাযোগ করব। সুতরাং যদি আপনার অ্যাপ্লিকেশনটি মাই সার্ভিসলিস্টনার প্রয়োগ করে, আপনি এটির সাথে আবদ্ধ হওয়ার পরে আপনি এটি আপনার পরিষেবাতে শ্রোতা হিসাবে নিবন্ধভুক্ত করতে পারেন, আপনার আবদ্ধ পরিষেবাদি থেকে শ্রোতাদের.অনআপডেটইউই কল করুন এবং সেখানে আপনার ইউআই আপডেট করুন!


এটি ঠিক যেমনটি আমি সন্ধান করছি তার মতো দেখাচ্ছে। আমাকে চেষ্টা করতে দাও। ধন্যবাদ।
ব্যবহারকারী200658

আপনার ক্রিয়াকলাপের কোনও রেফারেন্স ফাঁস না করার বিষয়ে সাবধান হন। কারণ আপনার ক্রিয়াকলাপটি নষ্ট হয়ে যেতে পারে এবং আবর্তিত অবস্থায় পুনরায় তৈরি করা যেতে পারে।
এরিক

হাই, দয়া করে এই লিঙ্কটি দেখুন। আমি এই জন্য একটি নমুনা কোড ভাগ করে নিয়েছি। ভবিষ্যতে কেউ সহায়ক বোধ করতে পারে এই ধারণা করে এখানে লিঙ্কটি রেখেছেন। myownandroid.blogspot.in/2012/08/…
jrhamza

+1 এটি একটি কার্যকর সমাধান, তবে আমার ক্ষেত্রে, আমার ক্রিয়াকলাপ চালিয়ে যাওয়ার জন্য সত্যই পরিষেবাটি প্রয়োজন যদিও সমস্ত ক্রিয়াকলাপ এটির সাথে আবদ্ধ থাকে (যেমন ব্যবহারকারী অ্যাপ্লিকেশনটি বন্ধ করে দেয়, প্রিমেচার ওএস সমাপ্তি), সম্প্রচারটি ব্যবহার করা ছাড়া আমার আর কোনও উপায় নেই রিসিভার।
নিয়ন ওয়ারেজ

9

আমি অটো , বিশেষ করে অ্যান্ড্রয়েডের জন্য উপযুক্তভাবে তৈরি করা একটি ইভেন্টবাস পরীক্ষা করে দেখার পরামর্শ দেব । আপনার ক্রিয়াকলাপ / ইউআই পরিষেবা থেকে বাসে পোস্ট করা ইভেন্টগুলি শুনতে পারে এবং ব্যাকএন্ড থেকে নিজেকে ডিকুয়াল করতে পারে।


5

ক্লাইডের সমাধানটি কাজ করে তবে এটি একটি সম্প্রচার, যা আমি নিশ্চিত যে সরাসরি কোনও পদ্ধতিতে কল করার চেয়ে কম দক্ষ হবে। আমার ভুল হতে পারে তবে আমি মনে করি সম্প্রচারটি আন্তঃ প্রয়োগের যোগাযোগের জন্য আরও বেশি বোঝানো হয়েছে।

আমি ধরে নিচ্ছি যে কোনও ক্রিয়াকলাপের সাথে কীভাবে কোনও পরিষেবা বাঁধতে হয় তা আপনি ইতিমধ্যে জানেন। এই ধরণের সমস্যাটি পরিচালনা করতে আমি নীচের কোডের মতো কিছু করি:

class MyService extends Service {
    MyFragment mMyFragment = null;
    MyFragment mMyOtherFragment = null;

    private void networkLoop() {
        ...

        //received new data for list.
        if(myFragment != null)
            myFragment.updateList();
        }

        ...

        //received new data for textView
        if(myFragment !=null)
            myFragment.updateText();

        ...

        //received new data for textView
        if(myOtherFragment !=null)
            myOtherFragment.updateSomething();

        ...
    }
}


class MyFragment extends Fragment {

    public void onResume() {
        super.onResume()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyFragment=this;
    }

    public void onPause() {
        super.onPause()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyFragment=null;
    }

    public void updateList() {
        runOnUiThread(new Runnable() {
            public void run() {
                //Update the list.
            }
        });
    }

    public void updateText() {
       //as above
    }
}

class MyOtherFragment extends Fragment {
             public void onResume() {
        super.onResume()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyOtherFragment=this;
    }

    public void onPause() {
        super.onPause()
        //Assuming your activity bound to your service
        getActivity().mMyService.mMyOtherFragment=null;
    }

    public void updateSomething() {//etc... }
}

থ্রেড সুরক্ষার জন্য আমি বিটগুলি রেখেছিলাম, যা প্রয়োজনীয়। পরিষেবাতে টুকরা রেফারেন্সগুলি পরীক্ষা করে এবং ব্যবহার করার সময় বা ব্যবহার করার সময় লকস বা এর মতো কিছু ব্যবহার নিশ্চিত করুন।


8
লোকালব্রডকাস্টম্যানেজারটি কোনও অ্যাপ্লিকেশনের মধ্যে যোগাযোগের জন্য ডিজাইন করা হয়েছে, তাই বেশ দক্ষ। সীমাবদ্ধ পরিষেবার পদ্ধতির ঠিক আছে যখন আপনার কাছে পরিষেবার জন্য সীমিত সংখ্যক ক্লায়েন্ট রয়েছে এবং পরিষেবাটি স্বাধীনভাবে চালানোর প্রয়োজন হয় না। স্থানীয় সম্প্রচার পদ্ধতির পরিষেবাটি তার ক্লায়েন্টদের থেকে আরও কার্যকরভাবে ডিক্লোল করার অনুমতি দেয় এবং থ্রেড-সুরক্ষাটিকে একটি অ-ইস্যু করে তোলে।
ক্লাইড

5
Callback from service to activity to update UI.
ResultReceiver receiver = new ResultReceiver(new Handler()) {
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        //process results or update UI
    }
}

Intent instructionServiceIntent = new Intent(context, InstructionService.class);
instructionServiceIntent.putExtra("receiver", receiver);
context.startService(instructionServiceIntent);

1

আমার সমাধানটি সবচেয়ে পরিষ্কার নাও হতে পারে তবে এটি কোনও সমস্যা ছাড়াই কাজ করা উচিত। যুক্তিটি কেবল আপনার ডেটা সঞ্চয় করার জন্য একটি স্ট্যাটিক ভেরিয়েবল তৈরি করা Serviceএবং আপনার প্রতি সেকেন্ডে আপনার ভিউ আপডেট করে Activity

আসুন বলে আপনি একটি আছে Stringআপনার এর উপর Serviceযে আপনি একটি থেকে এটি পাঠাতে চান TextViewআপনার এর উপর Activity। এটিকে ঐটির মত দেখতে হবে

আপনার পরিষেবা:

public class TestService extends Service {
    public static String myString = "";
    // Do some stuff with myString

আপনার ক্রিয়াকলাপ:

public class TestActivity extends Activity {
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tv = new TextView(this);
        setContentView(tv);
        update();
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    while (!isInterrupted()) {
                        Thread.sleep(1000);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                update();
                            }
                        });
                    }
                } catch (InterruptedException ignored) {}
            }
        };
        t.start();
        startService(new Intent(this, TestService.class));
    }
    private void update() {
        // update your interface here
        tv.setText(TestService.myString);
    }
}

2
কোনও পরিষেবা বা কোনও ক্রিয়াকলাপে কখনও স্থির পরিবর্তনশীল জিনিসটি করবেন না
মুর্তজা খুরশীদ হুসেন

@ মুর্তজাখুরশেদহুসাইন - আপনি কি এ সম্পর্কে আরও বিস্তারিত বলতে পারবেন?
সলিডসনেকে

2
স্ট্যাটিক সদস্যরা ক্রিয়াকলাপগুলিতে মেমরি ফাঁসের উত্স (চারপাশে প্রচুর নিবন্ধ রয়েছে) এবং সেগুলিতে রাখলে এটি আরও খারাপ হয়। ওপির পরিস্থিতিতে ব্রডকাস্ট রিসিভারটি অনেক বেশি উপযুক্ত বা অবিচ্ছিন্ন স্টোরেজ রাখাও একটি সমাধান।
মুর্তজা খুরশীদ হুসেন

@ মুর্তজাখুরশেদহুসাইন - সুতরাং আসুন আমি বলি যে আমার যদি ক্লাস থাকে (ডেটা পপুলেটিংয়ের জন্য আমি কোনও সার্ভিস ক্লাস না, কেবল কিছু মডেল ক্লাস) আমি স্থির হাশম্যাপের সাথে প্রতি মিনিটে কিছু ডেটা (একটি এপিআই থেকে আগত) নিয়ে পুনরায় জনবহুল হয়ে উঠি তবে এটি হিসাবে বিবেচিত হয় মেমরি লিক?
সলিডসনেকে

androidহ্যাঁর প্রসঙ্গে , যদি এটির একটি তালিকা থাকে তবে স্ক্যালাইট / রিয়েলএম ডিবি ব্যবহার করে একটি অ্যাডাপ্টার তৈরি করার চেষ্টা করুন বা ডেটা অবিরত রাখুন।
মুর্তজা খুরশীদ হুসেন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.