根據功能模塊劃分(Android開發推薦此方法)
- Activity mobilesafe.activty
- 后臺服務 mobilesafe.service
- 廣播接受者 mobilesafe.receiver
- 數據庫 mobilesafe.db.dao
- 對象(java bean) mobilesafe.domain/bean
- 自定義控件 mobilesafe.view
- 工具類 mobilesafe.utils
- 業務邏輯 mobilesafe.engine
閃屏頁面(Splash)作用:
- 展示logo,公司品牌
- 項目初始化
- 檢測版本更新
- 校驗程序合法性(比如:判斷是否有網絡,有的話才運行)
AndroidMinifest.xml 四大組件都需要在這里配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mxn.mobilesafe"
android:versionCode="1" //版本號
android:versionName="1.0" > //版本名
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
//項目所需的權限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-permission android:name="android.permission.CLEAR_APP_CACHE"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" > //主題
//activity的注冊
<activity
android:name="com.mxn.mobilesafe.activity.SplashActivity"
android:label="@string/app_name" >
<intent-filter> //起始的activity
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.mxn.mobilesafe.activity.HomeActivity" />
<activity android:name="com.mxn.mobilesafe.activity.SettingActivity" />
<activity android:name="com.mxn.mobilesafe.activity.LostFindActivity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.Setup1Activity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.Setup2Activity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.Setup3Activity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.Setup4Activity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.ContactActivity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.AtoolsActivity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.AddressActivity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.CallSafeActivity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.AppManagerActivity" >
</activity>
<activity android:name="com.mxn.mobilesafe.activity.TaskManagerActivity">
</activity>
<activity android:name="com.mxn.mobilesafe.activity.TaskManagerSettingActivity">
</activity>
<activity android:name="com.mxn.mobilesafe.activity.AntivirusActivity"></activity>
<activity android:name="com.mxn.mobilesafe.activity.AppLockActivity"></activity>
<activity android:name="com.mxn.mobilesafe.activity.CleanCacheActivity"></activity>
//廣播接收者的 注冊
<receiver android:name=".receiver.BootCompleteReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.SmsReceiver" >
<intent-filter android:priority="2147483647" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<!--
<receiver android:name=".receiver.OutCallReceiver" >靜態注冊的廣播
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
-->
//服務的注冊
<service android:name="com.mxn.mobilesafe.service.LocationService" >
</service>
<service android:name="com.mxn.mobilesafe.service.AddressService" >
</service>
<service android:name="com.mxn.mobilesafe.service.KillProcessService"></service>
<service android:name="com.mxn.mobilesafe.service.WatchDogService"></service>
</application>
</manifest>
activity_splash.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.mxn.mobilesafe.SplashActivity" android:background="@drawable/launcher_bg" android:id="@+id/rl_root"> <TextView android:id="@+id/tv_version" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="202dp" android:textSize="22sp" android:textColor="#000" android:shadowColor="#f00" //對版本號設置陰影 android:shadowDx="1" android:shadowDy="1" android:shadowRadius="1" android:text="版本號:1.0" /> <ProgressBar android:id="@+id/progressBar1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/tv_version" android:layout_centerHorizontal="true" android:layout_marginTop="54dp" /> <TextView android:id="@+id/tv_progress" android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:textColor="#f00" android:textSize="16sp" android:text="下載進度" /> </RelativeLayout>
SplashActivity.java
public class SplashActivity extends Activity {
protected static final int CODE_UPDATE_DIALOG;
protected static final int CODE_URL_ERROR;
protected static final int CODE_NET_ERROR;
protected static final int CODE_JSON_ERROR;
protected static final int CODE_ENTER_HOME;
private TextView tvVersion;
private TextView tvProgress;// 下載進度展示
// 服務器返回的信息
private String mversionName;// 版本名
private int mversionCode;// 版本號
private String mDesc;// 版本描述
private String mdowmloadurl;// 下載地址
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case CODE_UPDATE_DIALOG:
showUpdateDialog();//顯示升級對話框
break;
case CODE_URL_ERROR:
Toast.makeText(SplashActivity.this, "url錯誤", Toast.LENGTH_SHORT).show();
enterHome();
break;
case CODE_NET_ERROR:
Toast.makeText(SplashActivity.this, "網絡錯誤", Toast.LENGTH_SHORT).show();
enterHome();
break;
case CODE_JSON_ERROR:
Toast.makeText(SplashActivity.this, "json數據解析解析錯誤", Toast.LENGTH_SHORT).show();
enterHome();
break;
case CODE_ENTER_HOME:
enterHome();
break;
default:
break;
}
};
};
private SharedPreferences sp;
private RelativeLayout rlRoot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
tvVersion = (TextView) findViewById(R.id.tv_version);
tvProgress = (TextView) findViewById(R.id.tv_progress);// 默認隱藏
tvVersion.setText("版本號:" + getVersionCode());//給版本號設置內容,動態獲取的值
rlRoot = (RelativeLayout) findViewById(R.id.rl_root);
//判斷是否需要自動更新
sp = getSharedPreferences("config", MODE_PRIVATE);
boolean autoUpdate = sp.getBoolean("auto_update", true);
copyDB("address.db");//拷貝歸屬地查詢數據庫
copyDB("antivirus.db");//拷貝病毒庫
//更新病毒庫
updateVirus();
if(autoUpdate){
checkVersion();
}else{
mHandler.sendEmptyMessageDelayed(CODE_ENTER_HOME, 2000);
}
//閃屏頁漸變動畫效果
AlphaAnimation anim = new AlphaAnimation(0.3f, 1f);
anim.setDuration(2000);
rlRoot.startAnimation(anim);
}
//更新病毒數據庫
private void updateVirus() {
//聯網從服務器獲取到最近數據的MD5的特征碼
HttpUtils httputils = new HttpUtils();
String url = "http://172.28.3.112:8080/virus.json";
httputils.send(HttpMethod.GET, url, new RequestCallBack<String>(){
@Override
public void onFailure(HttpException arg0, String arg1) {
// TODO Auto-generated method stub
}
@Override
public void onSuccess(ResponseInfo<String> arg0) {
// TODO Auto-generated method stub
//System.out.println(arg0.result);
// JSONObject jsonobject = new JSONObject(arg0.result);
// String md5 = jsonobject.getString("md5");
// String desc = jsonobject.getString("desc");
}
});
}
// 獲取本地版本號
private int getVersionCode() {
PackageManager packgeManager = getPackageManager();//拿到包的管理者。。包管理器,獲取手機里面每個apk的信息(清單文件信息)
try {// 獲取包的信息。。 getPackageName()當前應用程序的包名 等于 package="com.mxn.mobilesafe"
PackageInfo packageInfo = packgeManager.getPackageInfo(getPackageName(), 0);
int versionCode = packageInfo.versionCode;
String versionName = packageInfo.versionName;
System.out.println("versionname=" + versionName + ";" + "versioncode=" + versionCode);
return versionCode;
} catch (NameNotFoundException e) {
// 沒有找到包名時
e.printStackTrace();
}
return -1;
}
// 從服務器獲取版本信息進行校驗
private void checkVersion() {
final long startTime = System.currentTimeMillis();
new Thread() {// 網絡訪問在分線程異步加載數據
public void run() {
Message msg = Message.obtain();
HttpURLConnection con = null;
try {// 本機地址:localhost 如果用模擬器加載本機的地址:用10.0.0.2來替換
URL url = new URL("http://10.0.2.2:8080/update.json");
// 打開連接
con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");//設置請求方法
con.setConnectTimeout(5000);// 設置連接超時,5S
con.setReadTimeout(5000);// 設置響應超時,鏈接上了,但服務器遲遲沒有響應
con.connect();// 鏈接服務器
int responseCode = con.getResponseCode();//獲取響應碼
if (responseCode == 200) {
// 獲取返回值
InputStream inputStream = con.getInputStream();
// 流轉化為字符串
String result = StreamUtils.readFormStream(inputStream);//自己定義的StreamUtils工具類
System.out.println("網絡結果返回:" + result);
//result是一個json字符串,進行解析
// 解析json
JSONObject jo = new JSONObject(result);
mversionName = jo.getString("versionName");//拿到服務器端的版本名
mversionCode = jo.getInt("versionCode");//拿到服務器端的版本號
mDesc = jo.getString("description");//拿到服務器端的版本描述
mdowmloadurl = jo.getString("downloadUrl");//拿到服務器端的下載鏈接
System.out.println(mDesc);
System.out.println(mversionCode);
// 服務器的大于 本地的,判斷是否有更新,如果大于 則有更新需要更新,彈出升級對話框
if (mversionCode > getVersionCode()) {
System.out.println("進行比較,有版本更新");
msg.what = CODE_UPDATE_DIALOG;
// showUpdateDialog();//這句是在子線程更新界面,android不能在子線程更新界面,要想在子線程更新界面所以用到handler.
} else {// 如果沒有版本更新
msg.what = CODE_ENTER_HOME;
}
}
} catch (MalformedURLException e) {// url錯誤的異常
msg.what = CODE_URL_ERROR;
e.printStackTrace();
} catch (IOException e) {//網絡錯誤異常
// 這個是可以攜帶數據的msg.obj =
msg.what = CODE_NET_ERROR;// what只是一個標識,用來區分消息!
e.printStackTrace();
} catch (JSONException e) {// json解析失敗
msg.what = CODE_JSON_ERROR;
e.printStackTrace();
} finally {
long endTime = System.currentTimeMillis();
long timeUsed = endTime - startTime;// 訪問網絡花費的時間
if (timeUsed < 2000) {
try {// 強制休眠2s,保證閃屏頁面2S
Thread.sleep(2000 - timeUsed);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mHandler.sendMessage(msg);// 消息發送出去,在handlemessage里進行相應的處理
if (con != null) {
con.disconnect();
}
}
}
}.start();
}
//升級對話框
private void showUpdateDialog() {
System.out.println("正在升級對話框");
// 升級對話框
AlertDialog.Builder builder = new AlertDialog.Builder(this);//context對象
builder.setTitle("最新版本" + mversionName);
builder.setMessage(mDesc);
// builder.setCancelable(false);//不讓用戶取消對話框,用戶體驗太差
builder.setPositiveButton("立即更新", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
System.out.println("立即更新");
// download方法
download();
}
});
builder.setNegativeButton("以后再說", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
enterHome();
}
});
builder.setOnCancelListener(new OnCancelListener() {
// 設置取消監聽,用戶點擊返回鍵時觸發
@Override
public void onCancel(DialogInterface dialog) {
enterHome();
}
});
builder.show();
}
protected void download() {// 下載服務器端的apk文件
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// 判斷是否有sd卡,sd卡掛載的時候才可以
tvProgress.setVisibility(View.VISIBLE);// 顯示進度
String target = Environment.getExternalStorageDirectory() + "/update.apk";//把文件下載到哪個路徑下,sd卡的根目錄
// xutils框架,使用HttpUtils工具下載文件,下載一個jar包
HttpUtils utils = new HttpUtils();
utils.download(mdowmloadurl, target, new RequestCallBack<File>() {
@Override // 文件下載進度
public void onLoading(long total, long current, boolean isUploading) {
// TODO Auto-generated method stub
super.onLoading(total, current, isUploading);
System.out.println("下載進度:" + current + "/" + total);
tvProgress.setText("下載進度:" + current * 100 / total + "%");
}
@Override
public void onSuccess(ResponseInfo<File> arg0) {
// TODO Auto-generated method stub
Toast.makeText(SplashActivity.this, "下載成功", Toast.LENGTH_SHORT).show();
// 下載完成之后,跳到系統的安裝界面。。Intent.ACTION_VIEW 是xml的action 標簽
Intent intent = new Intent(Intent.ACTION_VIEW);//系統的安裝界面
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(arg0.result),
"application/vnd.android.package-archive");
// startActivity(intent);
startActivityForResult(intent, 0);// 如果用戶取消安裝,會返回結果,回調方法onActivityResult,下文定義
}
@Override
public void onFailure(HttpException arg0, String arg1) {
// TODO Auto-generated method stub
Toast.makeText(SplashActivity.this, "下載失敗", Toast.LENGTH_SHORT).show();
}
});
} else {
Toast.makeText(SplashActivity.this, "沒有SD卡", Toast.LENGTH_SHORT).show();
}
}
@Override//用戶取消安裝,回調此方法
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
System.out.println("出現安裝界面,用戶點擊取消時。");
enterHome();
super.onActivityResult(requestCode, resultCode, data);
}
private void enterHome() {// 進入主界面
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
}
//拷貝數據庫,從assets目錄下拷貝到data/data/com.mxn.mobilesafe/files目錄下
private void copyDB(String dbName){
//獲取文件路徑
File destFile = new File(getFilesDir(),dbName);
if(destFile.exists()){
System.out.println("已存在");
}
FileOutputStream out = null;
InputStream in = null;
try {
in = getAssets().open(dbName);
out = new FileOutputStream(destFile);
int len = 0;
byte[] buffer = new byte[1024];
while((len = in.read(buffer))!=-1){
out.write(buffer,0,len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
in.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
StreamUtils.java
/*
* 讀取流的工具
* 把流對象轉換成字符串對象
*/
public class StreamUtils {
//將輸入流讀取成String后返回
public static String readFormStream(InputStream in) throws IOException{
// 定義字節數組輸出流對象
ByteArrayOutputStream out = new ByteArrayOutputStream();
// 定義讀取的長度
int len = 0 ;
// 定義讀取的緩沖區
byte[] buffer = new byte[1024];
// 按照定義的緩沖區進行循環讀取,直到讀取完畢為止
while((len=in.read(buffer))!=-1){
// 根據讀取的長度寫入到字節數組輸出流對象中
out.write(buffer,0,len);
}
String result = out.toString();
// 關閉流
in.close();
out.close();
return result;
// // 把讀取的字節數組輸出流對象轉換成字節數組
// byte data[] = out.toByteArray();
// // 按照指定的編碼進行轉換成字符串(此編碼要與服務端的編碼一致就不會出現亂碼問題了,android默認的編碼為UTF-8)
// return new String(data, "UTF-8");
}
}
系統安裝界面的activity的配置:
<activity android:name=".PackageInstallerActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/Theme.Transparent">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
我們服務器用的是tomcat,里面放置 新版本的apk和update.json:

將代碼打包為apk文件:

涉及的知識點:
PackageManager 包管理器,獲取手機里面每個apk的信息(清單文件信息)
版本更新流程:

網絡請求
> * URL
> * HttpUrlConntetion
JSON解析
> * JSONObject 專門用來解析json
> * JSONArray
對話框彈出
> AlertDialog
> AlertDialog.Builder
子線程更新UI
> * Handler + message
> * runOnUiThread(runnable)
頁面之間的跳轉Intent
GitHub 一個開源的網站,下載xUtils框架,將下載的jar包導入工程。
AlertDialog.Builder(this)
子類擁有父類的所有方法, 而且可以有更多自己的方法。父類無法有子類的方法
Activity(token), Context(沒有token)
平時,要獲取context對象的話, 優先選擇Activity, 避免bug出現, 盡量不用getApplicationContext()
activity是context的子類
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持億速云!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。