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键点击并不好用,对话框没消失,也没回到主界面。我估计是三星改了底层代码导致的。其他手机一切正常。就不上效果图了。希望对有需要的人有帮助。
- Android 用service实现不依赖activity的版本更新功能
- Android实现不依赖activity的dialog
- Service中调节屏幕亮度(不依赖Activity)的实现
- Android 版本更新功能的实现
- Android 不依赖activity的dialog
- android中不依赖activity的dialog弹窗的实现
- Android不依赖Activity的全局悬浮窗实现
- Android 不依赖activity的全局悬浮窗实现
- 不依赖activity的全局对话框的实现
- Android不依赖activity的全局弹框
- Android app“版本更新”功能的前后端实现
- Android通过DownloadManager实现App的版本更新功能
- 版本更新的Service
- android版本更新功能
- android 版本更新功能
- android版本更新功能
- Android版本更新功能
- Android 版本更新的实现
- java检测乱码原编码
- java 面向对象基础 UML图 构造方法 对象 参数传值 关联关系 依赖关系
- getopt的用法与optarg
- Linux的网卡由eth0变成了eth1,如何修复
- Bestcoder Pro.ID 2002 计算球体积
- Android 用service实现不依赖activity的版本更新功能
- Asp.net读取和写入txt文件方法
- EditText设置输入的类型,比如说限制只能输入字母和数字
- 凭证处理被批次处理冻结
- iOS 自定义常见第三方分享
- 关于百度地图申请key和混淆说明
- vs2013 创建调用链接库 没有生成 “.lib” 问题
- 函数类型的变量
- Android APK 中启动特定的 bin 服务