zoukankan      html  css  js  c++  java
  • Android(4.0.3+): Service, AsyncTask, 定时任务和UI通信

    Service使用AlarmManager实现后台定时任务

    在Android 4.0.3版本中, 使用AlarmManager实现后台定时任务是比较好的方案, 其实现机制, 是利用Service的 onStartCommand() 方法, 在每次被AlarmManager唤醒后, 执行任务并注册下一次唤醒到AlarmManager. 涉及的代码

    1. 新建DaemonService, 实现 onStartCommand() 方法, 在这个方法中新开线程执行任务, 并再次将AlarmReceiver注册到AlarmManager. 注: 同样的注册多次调用时, 不会注册多个, 而是会进行更新. 这个方法会在Activity中调用 startService(intent); 方法时被调用.

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            new DaemonThread().start();
            AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
            long triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000;
    
            Intent i = new Intent(this, AlarmReceiver.class);
            PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
            manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
            return super.onStartCommand(intent, flags, startId);
        }
    

     2. onDestroy() 方法在调用 Activity stopService(intent) 时会被调用, 此时需要将AlarmReceiver从AlarmManager中cancel掉.

        @Override
        public void onDestroy() {
            Log.d(TAG, "onDestroy");
            AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
            Intent i = new Intent(this, AlarmReceiver.class);
            PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
            manager.cancel(pi);
            super.onDestroy();
        }
    

    3. 新建Receiver, 用来注册到AlarmManager, 用于将来响应Alarm消息. 在内部的onReceive方法中, 启动DaemonService

    public class AlarmReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Intent i = new Intent(context, DaemonService.class);
            context.startService(i);
        }
    }
    

    4. 在AndroidManifest.xml中添加Service和Receiver

        <application
            <!--... -->
            <service android:name=".DaemonService" ></service>
            <receiver android:name=".AlarmReceiver" ></receiver>
        </application>

    5. 在Activity中, 对应控件的点击响应中添加service的启动, 停止代码

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // ...
    
            if (id == R.id.action_start) {
                Intent intent = new Intent(this, DaemonService.class);
                startService(intent);
                return true;
            }
    
            if (id == R.id.action_stop) {
                Intent intent = new Intent(this, DaemonService.class);
                stopService(intent);
                return true;
            }
    
            // ...
    
            return super.onOptionsItemSelected(item);
        }
    

    定时任务中使用 AsyncTask和 httpUrlConnection访问网址, 使用Callback进行结果回调

    1. 新建HttpAsyncCallback接口, 要接收AsyncTask返回数据的, 都要实现这个接口

    public interface HttpAsyncCallback {
        // This function will be called from inside of your AsyncTask when you are ready to callback to
        // your controllers (like a fragment, for example) The object in the completionHandler will be
        // whatever it is that you need to send to your controllers
        void completionHandler(Boolean success, int type, Object object);
    }
    

    2. 新建HttpAsyncTask类, 进行实际的HTTP访问

    public class HttpAsyncTask extends AsyncTask<String, Void, Void> {
        public static final int METHOD_GET = 0;
        public static final int METHOD_POST = 1;
    
        private static final String TAG = HttpAsyncTask.class.getSimpleName();
    
        private String postData;
        private int method;
        private int connectTimeout;
        private int readTimeout;
        private String encoding;
    
        private int type;
        private HttpAsyncCallback callback;
    
        public HttpAsyncTask(int method, String encoding) {
            this(null, method, encoding, 10000, 10000, 0, null);
        }
    
        public HttpAsyncTask(int method, String encoding, int type, HttpAsyncCallback callback) {
            this(null, method, encoding, 10000, 10000, type, callback);
        }
    
        public HttpAsyncTask(String postData, int method, String encoding, int type, HttpAsyncCallback callback) {
            this(postData, method, encoding, 10000, 10000, type, callback);
        }
    
        public HttpAsyncTask(String postData, int method, String encoding, int connectTimeout, int readTimeout, int type, HttpAsyncCallback callback) {
            this.postData = postData;
            this.method = method;
            this.encoding = encoding;
            this.connectTimeout = connectTimeout;
            this.readTimeout = readTimeout;
    
            this.type = type;
            this.callback = callback;
        }
    
        @Override
        protected Void doInBackground(String... strings) {
            Log.d(TAG, "Timestamp:" + System.currentTimeMillis());
            HttpURLConnection connection = null;
            try {
                connection = (HttpURLConnection) new URL(strings[0]).openConnection();
                connection.setConnectTimeout(connectTimeout);
                connection.setReadTimeout(readTimeout);
                if (method == METHOD_GET) {
                    connection.setRequestMethod("GET");
                } else {
                    // get请求的话默认就行了,post请求需要setDoOutput(true),这个默认是false的。
                    connection.setDoOutput(true);
                    connection.setRequestMethod("POST");
                    if (this.postData != null) {
                        OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
                        writer.write(postData);
                        writer.flush();
                    }
                }
    
                int statusCode = connection.getResponseCode();
                if (statusCode ==  200) {
                    InputStream in = connection.getInputStream();
                    byte[] bytes = getBytesByInputStream(in);
                    String response = new String(bytes, encoding);
                    Log.d(TAG, response);
                    // From here you can convert the string to JSON with whatever JSON parser you like to use
                    // After converting the string to JSON, I call my custom callback. You can follow this
                    // process too, or you can implement the onPostExecute(Result) method
                    // Use the response to create the object you need
                    if (callback != null) {
                        callback.completionHandler(true, type, "Timestamp:" + System.currentTimeMillis() + ", " + response);
                    }
                } else {
                    Log.d(TAG, statusCode+"");
                    if (callback != null) {
                        callback.completionHandler(false, type, statusCode);
                    }
                }
            } catch (IOException e) {
                Log.e(TAG, e.getMessage(), e);
            } finally {
                if (connection != null){
                    connection.disconnect();
                }
            }
            return null;
        }
    
        private byte[] getBytesByInputStream(InputStream is) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            try {
                while ((length = is.read(buffer)) != -1) {
                    bos.write(buffer, 0, length);
                }
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage());
                }
            }
            return bos.toByteArray();
        }
    
        public static String formDataToString(Map<String, String> data, String encoding) {
            StringBuilder sb = new StringBuilder();
            String con = "";
            for (String key : data.keySet()) {
                String value = data.get(key);
                try {
                    key = URLEncoder.encode(key, encoding);
                    value = URLEncoder.encode(value, encoding);
                    sb.append(con).append(key).append("=").append(value);
                    con = "&";
                } catch (UnsupportedEncodingException e) {
                    Log.e(TAG, "UnsupportedEncodingException " + encoding + " in processing:" + key);
                }
            }
            return sb.toString();
        }
    
        public static String formDataToJson(Map<String, String> data, String encoding) {
            if (data != null) {
                JSONObject jsonObject = new JSONObject(data);
                return jsonObject.toString();
            }
            return null;
        }
    }
    

    使用 Callback和BroadcastReceiver实现消息通信

    1. 在DaemonService中实现HttpAsyncCallback接口, 用于接收HttpAsyncTask任务执行结果

    public class DaemonService extends Service implements HttpAsyncCallback {
        private static final String TAG = DaemonService.class.getSimpleName();
    
        // ...
    
        @Override
        public void completionHandler(Boolean success, int type, Object object) {
            Log.d(TAG, "completionHandler");
        }
    }
    

    2. 在DaemonService的onStartCommand()方法中, 将自己做为参数传给HttpAsyncTask. 1和2是为了将AsyncTask的结果传回Service

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Map<String, String> map = new HashMap<>();
            map.put("phone", "13800138000");
            HttpAsyncTask task = new HttpAsyncTask(HttpAsyncTask.formDataToString(map, "UTF-8"), HttpAsyncTask.METHOD_POST, "UTF-8", 0, this);
            task.execute("https://www.toutiao.com/api/pc/realtime_news/");
            //new DaemonThread("").start();
            AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
            long triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000;
    
            Intent i = new Intent(this, AlarmReceiver.class);
            PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
            manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
            return super.onStartCommand(intent, flags, startId);
        }
    

    3. 为了从Service将结果传回Fragment, 需要在Fragment中注册一个BroadcastReceiver, 实现onReceive方法, 在这个方法中将结果更新到TextView, 在onCreateView中初始化这个receiver, 在onStart和onStop方法中进行注册和取消. 注意: 从fragment中获取TextView时, 需要在onActivityCreated方法中才行, 在其他的事件方法(onCreateView, onAttach中, findViewById拿到的是null

    public class MainActivityFragment extends Fragment {
        private static final String TAG = MainActivityFragment.class.getSimpleName();
        private BroadcastReceiver receiver;
        private TextView tv;
    
        ...
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            Log.d(TAG, "onCreateView");
            receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    String s = intent.getStringExtra("msg");
                    tv.setText(s);
                }
            };
            return inflater.inflate(R.layout.fragment_main, container, false);
        }
    
        @Override
        public void onAttach(Context context) {
            Log.d(TAG, "onAttach");
            super.onAttach(context);
        }
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            Log.d(TAG, "onActivityCreated");
            super.onActivityCreated(savedInstanceState);
            tv = getActivity().findViewById(R.id.sample_text);
        }
    
        @Override
        public void onStart() {
            Log.d(TAG, "onStart");
            super.onStart();
            if (receiver != null) {
                IntentFilter intentFilter = new IntentFilter(MainActivityFragment.class.getName() + ".TextView");
                getActivity().registerReceiver(receiver, intentFilter);
            }
        }
    
        @Override
        public void onStop() {
            Log.d(TAG, "onStop");
            if (receiver != null) {
                getActivity().unregisterReceiver(receiver);
            }
            super.onStop();
        }
    }
    

    代码在 https://github.com/MiltonLai/android-service-example

  • 相关阅读:
    GZS与小公园(DFS)
    II play with GG(思维规律)
    bearBaby loves sleeping(BFS)
    湖南大学新生赛C,G,J题解
    bootstrap 标签页的使用(tab)
    js 循环生成元素,并为元素添加click事件,结果只执行最后一个点击事件
    使用原生js实现一个列表数据展示页面不同的项目状态使整行显示不同颜色。
    【Vue】详解Vue组件系统 目录
    基于TCP与UDP协议的socket通信
    ElementUI三级菜单checkBox全选实现
  • 原文地址:https://www.cnblogs.com/milton/p/9378237.html
Copyright © 2011-2022 走看看