前一段时间开发了一个AppDemo软件,功能是在我开发的App中能直接修改手机自带浏览器的主页,增加,删除书签(书签缩图同步更换)等功能。过程坎坷,但总算按时完成了,今天就开发过程中遇到的问题做一个总结。
首先遇到的第一个难点:清除浏览器的缓存数。我们知道只要打开浏览器,那么浏览器的缓存数据就会保存在/data/data/com.android.browser中
这个时候不管你是Back键退出还是手机自动重启,浏览器的缓存数据依然存在。那么问题来了,要想修改系统自带的浏览器主页首先必须想办法清除浏览器的数据。怎么清除呢?接触过设置模块的朋友会知道,长按APP菜单桌面顶端会出现一个AppInfo,把菜单托进去之后窗口中会出现一个CLEARDATE的东西,对了,这个功能按键就是清楚数据的一个东东。所以,进入设置后看看它是怎么处理的。进入设置查找之后你会发现,其实很简单:就是根据App的包名进行清楚数据的。
首先看一下这个方法:
private void initiateClearUserData() {
// Invoke uninstall or clear user data based on sysPackage
String packageName = "com.android.browser";//系统自带浏览器的包名
Log.i(TAG, "Clearing user data for package : " +packageName);
if (mClearDataObserver == null) {
mClearDataObserver = new ClearUserDataObserver();
}
ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
boolean res = am.clearApplicationUserData(packageName,mClearDataObserver);
if (!res) {
// Clearing data failed for some obscure reason. Just log error fornow
Log.i(TAG, "Couldnt clear application user data forpackage:"+packageName);
}
}
看到这个方法估计你已经明白了,是不是就这个方法就行了呢,不!你要知道,Android系统的保护机制做的很好,当你改变系统数据时,会强加给你一个信息,就是说如果数据清除了会发送一个广播通知系统。看这个ClearUserDataObserver()方法
class ClearUserDataObserver extends IPackageDataObserver.Stub{
public voidonRemoveCompleted(final String packageName, final booleansucceeded) {
finalMessage msg = mHandler.obtainMessage(CLEAR_USER_DATA);
msg.arg1 =succeeded ? OP_SUCCESSFUL : OP_FAILED;
mHandler.sendMessage(msg);
}
}
CLEAR_USER_DATA这个标志位是干嘛用的呢,接着代码往下看
public static final boolean showShutdownDialog = false;
privatestatic final String TAG = "llcs";
privateClearUserDataObserver mClearDataObserver;
privatestatic final int CLEAR_USER_DATA = 1;
privatestatic final int OP_SUCCESSFUL = 1;
privatestatic final int OP_FAILED = 2;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// If the fragment is gone, don't process any more messages.
Log.d(TAG, "handle message : " + msg.what);
switch (msg.what) {
case CLEAR_USER_DATA:
processClearMsg(msg);
break;
default:
break;
}
}
};
private void processClearMsg(Message msg) {
int result = msg.arg1;
Log.i(TAG, "Cleared user data result:" + result);
//String packageName = mAppEntry.info.packageName;
String packageName = "com.android.browser";
if(result == OP_SUCCESSFUL) {
Log.i(TAG, "Cleared user data for package : "+packageName);
//mState.requestSize(mAppEntry.info.packageName);
// M ALPS00235193: send the clear success broadcast {@
Intent packageDataCleared = newIntent(Intent.ACTION_SETTINGS_PACKAGE_DATA_CLEARED);
packageDataCleared.putExtra("packageName", packageName);
this.sendBroadcast(packageDataCleared);
// @}
} else {
Log.i(TAG, "fail to clear user data for package : " +packageName);
}
//checkForceStop();
}
好了,到这里一切都明白了。根据App的包名然后处理数据最后把清除成功的信息通过广播发送出去.ACTION_SETTINGS_PACKAGE_DATA_CLEARED这个标志位就是模块数据被清理。然后通过广播发送出去。看一下清除之后再查看/data/data/com.android.browser下所包含的东西就没有了。
OK!不做过多解释,只要调用initiateClearUserData()方法就可以清除浏览器的缓存数据了。同样的道理,想要清楚其它模块的数据只要替换一下包名即可。清除缓存数据处理了,那么修改主页就简单多了。我的处理方式是写一个Settings.System.CHANGED_INTERNET,Settings.System.putString(MainActivity.this.getContentResolver(),Settings.System.CHANGED_INTERNET,"http://www.google.com");然后在浏览器中接收这个字符串替换原来的即可。
注意:这个Settings值需要自己定义,定义的路径在:frameworks/base/core/java/android/provider/Settings.java
第二个难点:修改书签时书签缩略图同步改变的问题
这个功能刚开始没有一点头绪,特别是书签缩略图同步改变的问题,还好浏览器的数据库是共用的,这样就好办多了,只有找到相应的字段就行了。
首先看一下怎么增加书签:假设此时添加的是baidu书签
private voidinsertBookMark() {
Resourcesres = this.getResources();
TypedArraypreloads = res.obtainTypedArray(R.array.bookmark_preloads);
ContentValues inputValue = new ContentValues();
//Bookmark值为1
inputValue.put(Browser.BookmarkColumns.BOOKMARK, 1);
//添加书签Title
inputValue.put(Browser.BookmarkColumns.TITLE, "baidu");
//添加书签URL
inputValue.put(Browser.BookmarkColumns.URL,
"http://www.baidu.com/");
intfaviconId = preloads.getResourceId(0, 0);
int thumbId = preloads.getResourceId(1, 0);
byte[] thumb= null, favicon = null;
try {
thumb = readRaw(res, thumbId);
} catch (IOException e) {
}
try {
favicon = readRaw(res, faviconId);
} catch (IOException e) {
}
inputValue.put(Browser.BookmarkColumns.FAVICON,
favicon);
//此为缩略图显示的图片
inputValue.put(Browser.BookmarkColumns.THUMBNAIL,
thumb);
//向浏览器添加该书签
ContentResolver contentResolver = getContentResolver();
contentResolver.insert(Browser.BOOKMARKS_URI, inputValue);
Toast.makeText(this, "添加成功!", Toast.LENGTH_LONG).show();
}
private byte[]readRaw(Resources res, int thumbId)throws IOException {
if (thumbId == 0) {
return null;
}
InputStream is = res.openRawResource(thumbId);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int read;
while ((read = is.read(buf)) > 0) {
bos.write(buf, 0, read);
}
bos.flush();
return bos.toByteArray();
} finally {
is.close();
}
//return null;
}
添加时有三点很关键:1 添加书签Title 。2添加书签URL 。 3 缩略图显示的图片 然后通过contentResolver.insert()方法添加到数据库中。1和2 不多数关键是3的实现。看一下这个东西R.array.bookmark_preloads。这个是什么?是浏览器所有增加书签的数组集合。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--需要把raw文件中的图片全部加入 add by lyj 20160119 -->
<array name="bookmark_preloads">
<item>@raw/site_navigation_default_default3</item> <!--baidu name -->
<item>@raw/site_navigation_default_default3</item> <!--baidu name -->
<item>@raw/favicon_google</item>
<item>@raw/thumb_google</item>
<item>@raw/favicon_picasa</item>
<item>@raw/thumb_picasa</item>
<item>@raw/favicon_yahoo</item>
<item>@raw/thumb_yahoo</item>
<item>@raw/favicon_msn</item>
<item>@raw/thumb_msn</item>
<item>@raw/favicon_twitter</item>
<item>@raw/thumb_twitter</item>
<item>@raw/favicon_facebook</item>
<item>@raw/thumb_facebook</item>
<item>@raw/favicon_wikipedia</item>
<item>@raw/thumb_wikipedia</item>
<item>@raw/favicon_ebay</item>
<item>@raw/thumb_ebay</item>
<item>@raw/favicon_cnn</item>
<item>@raw/thumb_cnn</item>
<item>@raw/favicon_nytimes</item>
<item>@raw/thumb_nytimes</item>
<item>@raw/favicon_espn</item>
<item>@raw/thumb_espn</item>
<item>@raw/favicon_amazon</item>
<item>@raw/thumb_amazon</item>
<item>@raw/favicon_weatherchannel</item>
<item>@raw/thumb_weatherchannel</item>
<item>@raw/favicon_bbc</item>
<item>@raw/thumb_bbc</item>
</array>
</resources>
有书签数组了,那个缩略图放哪呢?在res/目录下建一个raw文件,把所有能用到的书签图片都放进去即可。
然后看一下删除书签:
private void deleteBookMark() {
ContentResolver contentResolver = getContentResolver();
// 删除书签,String数组第一个对应TITLE,第二个对应URL。
int number =contentResolver.delete(Browser.BOOKMARKS_URI,
Browser.BookmarkColumns.TITLE + "=? and "
+Browser.BookmarkColumns.URL + "=?", new String[] {
"Google","http://www.google.com/" });
Log.i("lyj_bro", "number = " + number);
Toast.makeText(this, "删除成功!", Toast.LENGTH_LONG).show();
}
可以看到删除是通过URL进行删除的。删除后缩略图什么的会一并删除的。这个很简单。
同时还可以获取手机浏览器中所有的书签:
case R.id.get_homebook://获取所有书签的按键
listView.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_expandable_list_item_1, getData()));
break;
然后看这个方法getData():
private List getData() {
List data =new ArrayList();
//data.add
ContentResolver contentResolver = getContentResolver();
Cursorcursor = contentResolver.query(Browser.BOOKMARKS_URI, null,
null, null,null);
while(cursor.moveToNext()) {
if(cursor.getString(
cursor.getColumnIndex(Browser.BookmarkColumns.BOOKMARK))
.equals("1")) {
data.add(cursor.getString(cursor
.getColumnIndex(BookmarkColumns.TITLE)));
}
}
returndata;
}
需要在Oncreate()方法里加上
this.makeFileDir();
再看这个方法makeFileDir():
public voidmakeFileDir() {
FilebookMarkFile = newFile(Environment.getExternalStorageDirectory()
.getPath());
try {
if(!bookMarkFile.exists()) {
bookMarkFile.mkdir();
}
FileWriterfw = new FileWriter(bookMarkFile + File.separator
+ "my.txt",true);
Log.i("lyj_bro", "fw = " + fw );
ContentResolver contentResolver = getContentResolver();
Cursorcursor = contentResolver.query(Browser.BOOKMARKS_URI, null,
null, null,null);
while(cursor.moveToNext()) {
if(cursor
.getString(
cursor.getColumnIndex(Browser.BookmarkColumns.BOOKMARK))
.equals("1")) {
fw.write(cursor.getString(cursor
.getColumnIndex(BookmarkColumns.TITLE)) + "\n");
fw.write(cursor.getString(cursor
.getColumnIndex(BookmarkColumns.URL)) + "\n");
}
}
fw.close();
} catch(Exception e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
OK!这个功能不用解释,一看就明白了。
到这里一切就结束了。总之,看似简单的功能但用的东西却很多。总结起来供大家学习。
