android Service学习

菜鸟笔记,高手绕过。

文章内容列表:

1.分别通过startService和bindService的方式启动服务,比较两者不同,及在此期间发现的问题。

2.研究IntentService的 使用。


public class MyActivity extends Activity {
    
private String TAG="TAG";

    Button startButton,stopButton,startIntentServiceButton,stopIntentServiceButton,jumpButton;
    ServiceConnection mServiceConnection;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        startButton= (Button) findViewById(R.id.button);
        stopButton= (Button) findViewById(R.id.button2);
        startIntentServiceButton= (Button) findViewById(R.id.intentService_button);
        stopIntentServiceButton= (Button) findViewById(R.id.stop_intentService);
        jumpButton= (Button) findViewById(R.id.jump_button);
        jumpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              //  Intent intent=new Intent(MyActivity.this,SecondActivity.class);
               // startActivity(intent);
                MyActivity.this.finish();
                //android.os.Process.killProcess(android.os.Process.myPid());
            }
        });
        startIntentServiceButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.v(TAG,"startIntentServiceButton is pressed");
                MyIntentService myIntentService=new MyIntentService("myIntentService");
                Intent intent=new Intent(MyActivity.this,MyIntentService.class);
                startService(intent);
              // bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
            }
        });
        stopIntentServiceButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                Intent intent=new Intent(MyActivity.this,MyIntentService.class);
                stopService(intent);
                /**
                 * 当调用stopService方法时,service的onDestroy方法会立即被调用,但是服务里面新开的线程还在跑,直到跑完。
                 */
               // unbindService(mServiceConnection);
            }
        });
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.v(TAG, "BUTTON IS CLICKED");
               // startService(intent);
                /**
                 * IntentService的启动方式跟service的启动方式是一样的,但他自己管理自己的service,此处调用了startService方式后,会调用onHandleIntent方法
                 * onHandleIntent-->onDestroy.
                 * 也就是说IntentService自己处理完onHandleIntent方法后,就会自己结束自己。大无畏的精神啊,然后就调用onDestroy了。
                 */
                Intent intent = new Intent(MyActivity.this, MyService.class);


                IntentService intentService;
                Handler handler;
                bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
               // startService(intent);
                /***
                 * 调用startService之后,service调用onCreate 和onStartCommand方法,通过该方法启动的service会一直在运行,即使他的调用方已经结束了。
                 *所以如果使用这种方式启动,If you implement this, it is your responsibility to stop the service
              
                /***
                 * 调用bindService要传进去一个ServiceConnection 参数,bindService方法调用后会调用onCreate ,onBind方法,和onServiceConnected方法
                 * 调用次序是onCreate--->onBind----->onServiceConnected
                 * 通过该Binder可以实现Service和调用方的通信。
                 */
                /***
                 * 不管是startService还是bindService方式启动的服务。从启动服务的activity跳转到另一个activity,
                 * 该服务也还会继续跑。
                 * 但是两者的不同是:
                 * 当启动服务的activity destroy掉不会影响startService方式启动的service即使没有调用stopService,也不会有影响
                 * 但通过bindService启动的服务如果此时没有主动调用unBindService,然后就调用启动他的activity的finish方法,此时,该activity的
                 * onDestroy方法不会被调用,可能是因为资源没有释放的原因吧。如果此时你将你的程序按多任务键将其从历史任务中划掉,此时会调用onDestroy方法
                 * 当然也会报内存泄露的错误,因为之前没有调onDestroy方法就是因为资源没有释放,当然是猜测了。
                 * 但还有一种情况,如果你通过android.os.Process.killProcess(android.os.Process.myPid());
                 * 将程序杀死的话就不会报内存泄露的错误,也许该方法本身会帮你做些处理吧,但是activity的onDestroy方法依然不会被调用。
                 * 也就是说bindService方式启动的service是依附于启动他的activity的。
                 */
                Context context;


            }
        });
        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MyActivity.this,MyService.class);
                unbindService(mServiceConnection);
               // stopService(intent);
            }
        });
        mServiceConnection=new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                MyService.MyBinder myBinder= (MyService.MyBinder) service;
                int count=myBinder.getCount();
                Log.v(TAG,"COUNT IS "+count);
            }


            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.v(TAG,"!!1--onServiceDisconnected");
            }
        };
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.my, menu);
        return true;
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v(TAG,"---onDestroy");
    }
}

MyService类:

public class MyService extends Service {
    private static String TAG="TAG";
    MyBinder myBinder=new MyBinder();
    int count=0;
    public MyService() {
    }


    /***
     *
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        Log.v(TAG,"ONBIND");
        return myBinder;


    }
    class MyBinder extends Binder{


        public int getCount(){


            return count;
        }
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG,"SERVICE ONSTARTCOMMAND");
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG,"service onDestroy");
    }


    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG,"SERVICE ONCREATE");
    }
}

MyIntentService类:

public class MyIntentService extends IntentService {
    private static String TAG="TAG";
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super("MyIntentService");
    }
    public MyIntentService(){
        super("MyIntentService");
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        Log.v(TAG,"onHandleIntent");
        /*long futrueTime=System.currentTimeMillis()+5000;
        //执行5s钟。
        while (System.currentTimeMillis()<futrueTime) {
            Log.v(TAG, "ONHANDLEINTENT");
            synchronized (this) {
                try {
                    Log.v(TAG,"WAIT");
                    wait(1000);


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }*/
        for (int i=0;i<15;i++){
            Log.v(TAG,"ONHandleThread "+i);
            try {
                Log.v(TAG,"CurrentThread id is "+Thread.currentThread().getName());
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG,"----onStartCommand");
        return super.onStartCommand(intent, flags, startId);


    }


    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG,"----onCreate");
    }


    @Override
    public void onStart(Intent intent, int startId) {


        super.onStart(intent, startId);
        Log.v(TAG,"----INTENTSERVICE ONSTART");
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG,"===onDestroy");
    }

}

例子中有5个按钮,startButton和stopButton测试startService和bindService,startIntentService和stopIntentService测试IntentService.jumpButton来结束启动服务的activity.具体说明例子中有注释。

1.1通过startService方法启动的服务跟启动方没有关系,即使启动方destroy了,服务依然跑。

1.2但通过bindService启动的服务,必须在activity 调用destroy之前释放资源,unBindService,不然会造成资源泄露。

1.3像上面例子中bindService之后,如果按停止按钮去unBindService,直接按jumpButton去结束当前activity,你会发现当前activity的onDestroy方法并没有被调到。此处注意下。

1.4此时从后台任务里将应用划掉会报内存泄露的错误。如果用android.os.Process.killProcess(android.os.Process.myPid());去结束掉整个应用,你会发现不会报内存泄露的错误,即使没有调用unBindService方法。

2.IntentService是Service的子类,也需要在manifest里面进行注册,其中要注意MyIntentService必须要有一个空的构造函数,如代码中实现,否则注册会编译不过。

3.普通Service类是运行在主线程里的,要进行耗时操作需要自己新开线程,但IntentService可以进行耗时操作,直接在onHandleIntent方法里进行耗时操作就好。耗时操作完成后自动调用onDestroy方法,这一点注意,如果在进行耗时操作时你显式调用stopService的方式结束IntentService,这时IntentService的onDestroy方法会立即被调用,但耗时操作还是会继续执行,直到执行结束。这一点的原理可以从IntentService的源码中看出:

 

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;


    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }


        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }


    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }


    /**
     * Sets intent redelivery preferences.  Usually called from the constructor
     * with your preferred semantics.
     *
     * <p>If enabled is true,
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
     * {@link #onHandleIntent(Intent)} returns, the process will be restarted
     * and the intent redelivered.  If multiple Intents have been sent, only
     * the most recent one is guaranteed to be redelivered.
     *
     * <p>If enabled is false (the default),
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
     * dies along with it.
     */
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }


    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.


        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();


        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }


    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }


    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }


    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


    /**
     * Unless you provide binding for your service, you don't need to implement this
     * method, because the default implementation returns null. 
     * @see android.app.Service#onBind
     */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     */
    protected abstract void onHandleIntent(Intent intent);

}

从源码中可以看出,IntentService内部新开启了一个IntentThread的线程来处理耗时操作。当我们通过startService的方式启动IntentService时,会调用其的onStart和onStartCommand方法,这两个方法里面会去将你启动IntentService时所用的Intent传给Message的obj参数,然后将其发送给其内部的handler,在handler的handleMessage方法里面就去调用handleIntent方法了,这就是为什么我们启动服务后IntentService就会去调用onHandleIntent方法的原因了。同时我们发现在handleMessage方法里面onHandleIntent方法结束完之后,intentService就会调用stopSelf方法结束自己。IntentService的源码还是非常简单的,相信大家都能看懂,记录仅为后来复习所用。若有错误,欢迎拍砖。代码下载地址:https://github.com/happycodinggirl/AndroidLearningProcess.git



你可能感兴趣的:(android Service学习)