Android 用service实现不依赖activity的版本更新功能

来源:互联网 发布:淘宝衣服评价语 编辑:程序博客网 时间:2024/04/30 12:40
    做Android开发的同学可能都很熟悉service了,四大组件之一吗,在后台运行没那么容易被杀死,可以处理一些不用显示在前台界面操作和功能。比如,音乐播放器的后台实现,网络下载等工作。我要写的是,关于app的版本检测和更新功能在service里的实现。    先介绍下项目背景。大部分app都需要提供一个在线更新的功能,一般有自动弹出对话框,或者点击某个按钮后,弹出对话框提示有新版本的更新。一般来说,我们会在app的第一个activity的oncreate方法里,开启一条线程,去访问服务器,得到一个json或者xml数据,然后解析,判断是否有新版本。如果有,弹出对话框提示用户。好像没什么问题,开始我也是这么做的。后来,出现了问题后才使用现在的解决方案的。    我们设想一下,这样的流程中弹出的对话框,是依赖于这个界面的,也就是这个activity的,对吧,而网络访问,解析,是耗时的操作,如果,用户每次在你的更新界面不做多的停留,那么他是不是永远都不知道有新版本了?再想的糟糕点,如果这个界面跟欢迎界面一样,就只出现一次,跳过了就finish掉了,或者是被系统回收了。那就不是看不到这个对话框了,你的程序会崩掉。因为你的dialog依赖于这个activity。怎么样能较好的处理上述几个问题呢。没错,这个时候你可以考虑系统对话框,完全独立的对话框,不依赖于activity。甚至,你都不要在activity里去调用这个系统对话框,让她和活动彻底分手。    你可以写一个继承自Application的类,在这个类里来操作和控制dialog。但是控制力度没有在service里来操作更方便自如。废话有点多了。说说具体要实现的东西。在service里,开启网络访问下载文件,这里我用的是okhttp框架。解析json数组,版本号小于解析的版本信息,就弹出对话框。上代码:    package net.xprinter.utils;

import java.io.File;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;

import net.xprinter.R;
import net.xprinter.entity.VersionEntity;
import net.xprinter.testview.HomeListener;
import net.xprinter.testview.HomeListener.OnHomePressedListener;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;

public class DownLoadService extends Service{
public static boolean isRunning=false;
//ok3的构建对象方法
private static OkHttpClient client=new OkHttpClient.Builder().connectTimeout(6, TimeUnit.SECONDS).build();

private HomeListener mHomeListener;Handler mHandler;Dialog dialog;@Overridepublic IBinder onBind(Intent intent) {    // TODO Auto-generated method stub    return null;}@Overridepublic void onCreate() {    // TODO Auto-generated method stub    //Log.i("TAG","onCreateservice");    super.onCreate();    //此处代码是监听home键用的,系统会话框在按下home后并不会消失,监听home键,调用dialog.cancle;    mHomeListener=new HomeListener(getApplicationContext());    mHomeListener.setOnHomePressedListener(new OnHomePressedListener() {        @Override        public void onHomePressed() {            // TODO Auto-generated method stub            if (dialog!=null) {                dialog.cancel();            }        }        @Override        public void onHomeLongPressed() {            // TODO Auto-generated method stub            if (dialog!=null) {                dialog.cancel();            }        }    });    mHomeListener.startWatch();    //对于异步网络任务,回调方法里与主线程交互用的handler    mHandler=new Handler(){        public void handleMessage(android.os.Message msg) {            int msgid=msg.what;            Bundle bundle=msg.getData();            switch (msgid) {            case UpdataBiz.version_error://网络请求onfailed回调                Toast.makeText(getApplicationContext(), R.string.genxinshibai, 0).show();                stopSelf();                break;            case UpdataBiz.error_msg://网络请求onfailed回调                Toast.makeText(getApplicationContext(), R.string.genxinshibai, 0).show();                stopSelf();                break;            case UpdataBiz.version_msg://网络访问成功,handler比较版本            //json解析的版本信息实体类                final VersionEntity entity=(VersionEntity) bundle.getSerializable("version");

//得到当前版本号的方法
try {
PackageManager manager=getPackageManager();
String packageName=getPackageName();
PackageInfo packageInfo;
packageInfo = manager.getPackageInfo(packageName, 0);

                    if (packageInfo.versionCode<Integer.parseInt(entity.getVersion())) {

//构建系统对话框,注意第一个参数context,不再与activity有关,第二个是dialog样式
Builder builder=new Builder(getApplicationContext(),R.style.dialog);
builder.setIcon(android.R.drawable.btn_star);
builder.setTitle(R.string.BMUpdateTitle);
builder.setMessage(entity.getChangeLog());

                        builder.setPositiveButton(R.string.BMUpdateNow, new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                // TODO Auto-generated method stub                                //biz类的下载方法                                UpdataBiz.downLoad(mHandler, client, entity.getApkUrl());                            }                        });                        builder.setNeutralButton(R.string.BMNotNow, new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                // TODO Auto-generated method stub                                stopSelf();//不下载,就关掉服务service,避免占用资源                            }                        });                        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {

//重写dialog的cancle方法,dialog消失,服务关闭,写不写随你需要而定
@Override
public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
stopSelf();
}
});
dialog=builder.create();

    //系统dialog最终要的一行代码,还有需要一个权限android.permission.SYSTEM_ALERT_WINDOW,不然会报错                 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);                        dialog.show();                    }else {                        Toast.makeText(getApplicationContext(), R.string.no_updata, 0).show();                        stopSelf();                    }                } catch (NameNotFoundException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                break;            case UpdataBiz.download_msg:                //将下载的文件执行安装,并关闭服务                String apkPath=bundle.getString("apkpath");                File file=new File(apkPath);                Intent intent=new Intent(Intent.ACTION_VIEW);                Uri uri=Uri.fromFile(file);                String type="application/vnd.android.package-archive";                intent.setDataAndType(uri, type);                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                 startActivity(intent);                stopSelf();                break;            default:                break;            }        };    };}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    // TODO Auto-generated method stub    //Log.i("TAG","onStartservice");    isRunning=true;

//biz类里的获取版本信息的网络请求
UpdataBiz.getNewVersion(mHandler, client);

    return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {    // TODO Auto-generated method stub    super.onDestroy();    isRunning=false;

//home键的监听关闭,home键在2.3版本后要用广播才能监听,这个封装好的home监听类是网上找的,源码也会贴上,不过注明这个不是自己写的。
mHomeListener.stopWatch();

    //Log.i("TAG", "onDestroyserver");}

}
执行网络请求的biz类:
package net.xprinter.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

import com.alibaba.fastjson.JSON;

import net.xprinter.entity.VersionEntity;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class UpdataBiz {
//handler发消息的几个常量
public static final int error_msg=0;
public static final int version_msg=1;
public static final int download_msg=2;
public static final int version_error=4;
public static void downLoad(final Handler handler,OkHttpClient client,String url){
//ok请求的写法,因为不是介绍okhttp,就不多注释了
Request request=new Request.Builder().url(url).build();

    client.newCall(request).enqueue(new Callback() {        final String target="/mnt/sdcard/"+UUID.randomUUID().toString()+".apk";//访问失败        @Override        public void onFailure(Call arg0, IOException arg1) {            // TODO Auto-generated method stub            Message message=handler.obtainMessage();            message.what=error_msg;            handler.sendMessage(message);        }

//请求成功得到文件流,写入到指定文件
@Override
public void onResponse(Call arg0, Response response) throws IOException {
// TODO Auto-generated method stub
Log.i(“TAG”,”“+response.body().contentLength());
InputStream is=null;
FileOutputStream fo=null;
byte[] b=new byte[1024*2];
int len=0;
try {
is=response.body().byteStream();

                File f=new File(target);                fo=new FileOutputStream(f);                while ((len=(is.read(b)))!= -1) {                    fo.write(b,0,len);                }            } catch (Exception e) {                // TODO Auto-generated catch block                e.printStackTrace();            }finally{                try {                    if (is!=null) {                        is.close();                    }                    if (fo!=null) {                        fo.close();                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        //faxiaoxi            Message message=handler.obtainMessage();            message.what=download_msg;            Bundle bundle=new Bundle();            bundle.putString("apkpath", target);            message.setData(bundle);            handler.sendMessage(message);        }    });}//请求获得版本信息public static void getNewVersion(final Handler handler,OkHttpClient client){    String url="http://www.xprinter.net/upload/apkinfo.txt";    Request request = new Request.Builder().url(url).build();    client.newCall(request).enqueue(new Callback() {        @Override        public void onFailure(Call arg0, IOException arg1) {            // TODO Auto-generated method stub            Message message=handler.obtainMessage();            message.what=version_error;            handler.sendMessage(message);        }        @Override        public void onResponse(Call arg0, Response response) throws IOException {            // TODO Auto-generated method stub            String json=response.body().string();            //json=new String(json.getBytes("ANSI"),"UTF-8");            Log.i("TAG", json);            //瑙f瀽json            Message message=handler.obtainMessage();            VersionEntity entity=new VersionEntity();            try {                entity = JSON.parseObject(json, VersionEntity.class);                Log.i("TAG", entity.getApkUrl());                message.what=version_msg;                Bundle bundle =new Bundle();                bundle.putSerializable("version", entity);                message.setData(bundle);                handler.sendMessage(message);            } catch (Exception e) {                // TODO Auto-generated catch block                e.printStackTrace();                message.what=version_error;                handler.sendMessage(message);            }        }    });}

}

//监听home键的类:
此类为网络上搬来的,不是本人原创
package net.xprinter.testview;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

/**
* Home键监听封装
*
*
*/
public class HomeListener {

static final String TAG = "HomeListener"; private Context mContext; private IntentFilter mFilter; private OnHomePressedListener mListener; private InnerRecevier mRecevier; // 回调接口  public interface OnHomePressedListener {     public void onHomePressed();     public void onHomeLongPressed(); } public HomeListener(Context context) {     mContext = context;     mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); } /** * 设置监听 *  * @param listener */ public void setOnHomePressedListener(OnHomePressedListener listener) {     mListener = listener;     mRecevier = new InnerRecevier(); } /** * 开始监听,注册广播 */ public void startWatch() {     if (mRecevier != null) {         mContext.registerReceiver(mRecevier, mFilter);     } } /** * 停止监听,注销广播 */ public void stopWatch() {     if (mRecevier != null) {         mContext.unregisterReceiver(mRecevier);     } } /** * 广播接收者 */ class InnerRecevier extends BroadcastReceiver {     final String SYSTEM_DIALOG_REASON_KEY = "reason";     final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";     final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";     final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";     final String SYSTEM_DIALOG_REASON_ASSIST = "assist";    @Override     public void onReceive(Context context, Intent intent) {         String action = intent.getAction();         if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {             String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);             if (reason != null) { 

// Log.e(TAG, “action:” + action + “,reason:” + reason);
if (mListener != null) {
if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
// 短按home键
mListener.onHomePressed();
} else if (reason
.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
// 长按home键
mListener.onHomeLongPressed();
} else if (SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) {
// samsung 长按Home键
mListener.onHomeLongPressed();
}
}
}
}
}
}
}

    好了,这样的话,你只需要在你需要启动服务的地方,startservice()就可以开启服务了。执行你要的功能,并能很好的隔离出这个功能,不依赖于界面。注意:service里的代码也是运行在主线程的,要注意多线程的使用。最后说一下我的实现后的效果。当系统对话框弹出的时候,我的欢迎界面还未结束,欢迎界面结束后跳转到另一界面,而这个系统对话框完全不受影响。    也有一个bug,分享出来,在三星s7上,home键点击并不好用,对话框没消失,也没回到主界面。我估计是三星改了底层代码导致的。其他手机一切正常。就不上效果图了。希望对有需要的人有帮助。
1 0
原创粉丝点击