最终效果
HelloWord工程应用代码:
package com.pateo; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; import com.pateo.R; import android.content.pm.PackageManager; public class HelloWordActivity extends Activity { TextView tv ; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView)findViewById(R.id.tv); PackageManager pm = getPackageManager(); try{ tv.setText("result : " + pm.getAppVoiceEntryInfo("packageName", "xmlName", "bnfName").speechContent); }catch( android.content.pm.PackageManager.NameNotFoundException e){ tv.setText("" + e); } } }上面getAppVoiceEntryInfo不是PackageManager自带的方法是我自己添加的方法
我是自己写了个HelloWord工程,放入到源码中,自己的应用怎么放入框架中要遵循:修改/bulid/target/product/generic.mk 把工程编译到系统中
generic.mk # # Copyright (C) 2007 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is a generic product that isn't specialized for a specific device. # It includes the base Android platform. PRODUCT_PACKAGES := \ AccountAndSyncSettings \ CarHome \ DeskClock \ AlarmProvider \ Bluetooth \ Calculator \ Calendar \ Camera \ CertInstaller \ DrmProvider \ Email \ Gallery3D \ LatinIME \ Launcher2 \ Mms \ Music \ Provision \ Protips \ QuickSearchBox \ Settings \ Sync \ Updater \ CalendarProvider \ SyncProvider \ Helloworld
HelloWord的Android.mk文件
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := HelloWord LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) # Use the folloing include to make our test apk. include $(call all-makefiles-under,$(LOCAL_PATH))
把HelloWord工程放入package/apps/目录下面,在此工程的assets木下建立一个xml文件里面写一些内容,我在这里建的文件名:voice_recognition.xml
到这里基本应用的代码完成了
下面是框架中的一些实现步骤
Ctrl+Shift+T[Eclipse的快捷键方式,下同]搜索类:PackageManager为其添加如下代码:
/** * 语音识别获取应用的识别词条的动作的启动信息 * @param packageName 应用的包名 * @param xmlName 词条对应的动作定义的文件 * @param bnfName 词条识别的入科大库的文件 * @return * @throws NameNotFoundException */ public abstract AppVoiceEntryInfo getAppVoiceEntryInfo(String packageName, String xmlName,String bnfName)throws NameNotFoundException;PackageManager是个抽象类,我们F4找到它的实现类之一:MockPackageManager为其添加如下代码:
@Override public AppVoiceEntryInfo getAppVoiceEntryInfo(String packageName, String xmlName, String bnfName) throws NameNotFoundException { // TODO Auto-generated method stub throw new UnsupportedOperationException(); }找到PackageManager的实现类之二:MockPackageManager为其添加如下代码:
@Override public AppVoiceEntryInfo getAppVoiceEntryInfo(String packageName, String xmlName, String bnfName) throws NameNotFoundException { try { AppVoiceEntryInfo appVoiceEntryInfo = mPM.getAppVoiceEntryInfo( "packageName", "xmlName", "bnfName"); Log.d(TAG, "============================getAppVoiceEntryInfo===================================="); return appVoiceEntryInfo; } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } }上面的mPM的定义:
private final IPackageManager mPM;
它是怎么被赋值的呢Ctril+F找“mPM =”,找到如下赋值的地方:
ApplicationPackageManager(ContextImpl context, IPackageManager pm) { mContext = context; mPM = pm; }
通过Ctril+Shift+G找到ApplicationPackageManager(ContextImpl context, IPackageManager pm)这个构造方式是在哪被调用的即初始化的
@Override public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; }
接着找pm是怎么来的,点击getPackageManager()这个方法,进入ActivityThread有关这个方法的实现,代码如下:
public static IPackageManager getPackageManager() { if (sPackageManager != null) { //Slog.v("PackageManager", "returning cur default = " + sPackageManager); return sPackageManager; } IBinder b = ServiceManager.getService("package"); //Slog.v("PackageManager", "default service binder = " + b); sPackageManager = IPackageManager.Stub.asInterface(b); //Slog.v("PackageManager", "default service = " + sPackageManager); return sPackageManager; }
从上面我们可以知道了,我们需要找到IPackageManager.aidl文件,通过Ctril+H找到这个aidl文件,当然你也可以在Ubuntu下通过grep过滤来找到这个aidl文件,个人习惯不同
这个aidl文件中加一个 AppVoiceEntryInfo getAppVoiceEntryInfo(String packageName, String xmlName, String bnfName);接口方法
/* ** ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ package android.content.pm; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.ProviderInfo; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.Uri; import android.content.IntentSender; import android.content.pm.AppVoiceEntryInfo; /** * See {@link PackageManager} for documentation on most of the APIs * here. * * {@hide} */ interface IPackageManager { AppVoiceEntryInfo getAppVoiceEntryInfo(String packageName, String xmlName, String bnfName); PackageInfo getPackageInfo(String packageName, int flags); int getPackageUid(String packageName); int[] getPackageGids(String packageName); String[] currentToCanonicalPackageNames(in String[] names); String[] canonicalToCurrentPackageNames(in String[] names); PermissionInfo getPermissionInfo(String name, int flags); List<PermissionInfo> queryPermissionsByGroup(String group, int flags); PermissionGroupInfo getPermissionGroupInfo(String name, int flags); List<PermissionGroupInfo> getAllPermissionGroups(int flags); ApplicationInfo getApplicationInfo(String packageName, int flags); ActivityInfo getActivityInfo(in ComponentName className, int flags); ActivityInfo getReceiverInfo(in ComponentName className, int flags); ServiceInfo getServiceInfo(in ComponentName className, int flags); int checkPermission(String permName, String pkgName); int checkUidPermission(String permName, int uid); boolean addPermission(in PermissionInfo info); void removePermission(String name); boolean isProtectedBroadcast(String actionName); int checkSignatures(String pkg1, String pkg2); int checkUidSignatures(int uid1, int uid2); String[] getPackagesForUid(int uid); String getNameForUid(int uid); int getUidForSharedUser(String sharedUserName); ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags); List<ResolveInfo> queryIntentActivities(in Intent intent, String resolvedType, int flags); List<ResolveInfo> queryIntentActivityOptions( in ComponentName caller, in Intent[] specifics, in String[] specificTypes, in Intent intent, String resolvedType, int flags); List<ResolveInfo> queryIntentReceivers(in Intent intent, String resolvedType, int flags); ResolveInfo resolveService(in Intent intent, String resolvedType, int flags); List<ResolveInfo> queryIntentServices(in Intent intent, String resolvedType, int flags); List<PackageInfo> getInstalledPackages(int flags); List<ApplicationInfo> getInstalledApplications(int flags); /** * Retrieve all applications that are marked as persistent. * * @return A List<applicationInfo> containing one entry for each persistent * application. */ List<ApplicationInfo> getPersistentApplications(int flags); ProviderInfo resolveContentProvider(String name, int flags); /** * Retrieve sync information for all content providers. * * @param outNames Filled in with a list of the root names of the content * providers that can sync. * @param outInfo Filled in with a list of the ProviderInfo for each * name in 'outNames'. */ void querySyncProviders(inout List<String> outNames, inout List<ProviderInfo> outInfo); List<ProviderInfo> queryContentProviders( String processName, int uid, int flags); InstrumentationInfo getInstrumentationInfo( in ComponentName className, int flags); List<InstrumentationInfo> queryInstrumentation( String targetPackage, int flags); /** * Install a package. * * @param packageURI The location of the package file to install. * @param observer a callback to use to notify when the package installation in finished. * @param flags - possible values: {@link #FORWARD_LOCK_PACKAGE}, * {@link #REPLACE_EXISITING_PACKAGE} * @param installerPackageName Optional package name of the application that is performing the * installation. This identifies which market the package came from. */ void installPackage(in Uri packageURI, IPackageInstallObserver observer, int flags, in String installerPackageName); void finishPackageInstall(int token); /** * Delete a package. * * @param packageName The fully qualified name of the package to delete. * @param observer a callback to use to notify when the package deletion in finished. * @param flags - possible values: {@link #DONT_DELETE_DATA} */ void deletePackage(in String packageName, IPackageDeleteObserver observer, int flags); String getInstallerPackageName(in String packageName); void addPackageToPreferred(String packageName); void removePackageFromPreferred(String packageName); List<PackageInfo> getPreferredPackages(int flags); void addPreferredActivity(in IntentFilter filter, int match, in ComponentName[] set, in ComponentName activity); void replacePreferredActivity(in IntentFilter filter, int match, in ComponentName[] set, in ComponentName activity); void clearPackagePreferredActivities(String packageName); int getPreferredActivities(out List<IntentFilter> outFilters, out List<ComponentName> outActivities, String packageName); /** * As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}. */ void setComponentEnabledSetting(in ComponentName componentName, in int newState, in int flags); /** * As per {@link android.content.pm.PackageManager#getComponentEnabledSetting}. */ int getComponentEnabledSetting(in ComponentName componentName); /** * As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}. */ void setApplicationEnabledSetting(in String packageName, in int newState, int flags); /** * As per {@link android.content.pm.PackageManager#getApplicationEnabledSetting}. */ int getApplicationEnabledSetting(in String packageName); /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage * on the device is greater than or equal to the requested * free storage, no cache files are cleared. If the currently * available storage on the device is less than the requested * free storage, some or all of the cache files across * all applications are deleted (based on last accessed time) * to increase the free storage space on the device to * the requested value. There is no guarantee that clearing all * the cache files from all applications will clear up * enough storage to achieve the desired value. * @param freeStorageSize The number of bytes of storage to be * freed by the system. Say if freeStorageSize is XX, * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. * @param observer call back used to notify when * the operation is completed */ void freeStorageAndNotify(in long freeStorageSize, IPackageDataObserver observer); /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage * on the device is greater than or equal to the requested * free storage, no cache files are cleared. If the currently * available storage on the device is less than the requested * free storage, some or all of the cache files across * all applications are deleted (based on last accessed time) * to increase the free storage space on the device to * the requested value. There is no guarantee that clearing all * the cache files from all applications will clear up * enough storage to achieve the desired value. * @param freeStorageSize The number of bytes of storage to be * freed by the system. Say if freeStorageSize is XX, * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. */ void freeStorage(in long freeStorageSize, in IntentSender pi); /** * Delete all the cache files in an applications cache directory * @param packageName The package name of the application whose cache * files need to be deleted * @param observer a callback used to notify when the deletion is finished. */ void deleteApplicationCacheFiles(in String packageName, IPackageDataObserver observer); /** * Clear the user data directory of an application. * @param packageName The package name of the application whose cache * files need to be deleted * @param observer a callback used to notify when the operation is completed. */ void clearApplicationUserData(in String packageName, IPackageDataObserver observer); /** * Get package statistics including the code, data and cache size for * an already installed package * @param packageName The package name of the application * @param observer a callback to use to notify when the asynchronous * retrieval of information is complete. */ void getPackageSizeInfo(in String packageName, IPackageStatsObserver observer); /** * Get a list of shared libraries that are available on the * system. */ String[] getSystemSharedLibraryNames(); /** * Get a list of features that are available on the * system. */ FeatureInfo[] getSystemAvailableFeatures(); boolean hasSystemFeature(String name); void enterSafeMode(); boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); /** * Ask the package manager to perform dex-opt (if needed) on the given * package, if it already hasn't done mode. Only does this if running * in the special development "no pre-dexopt" mode. */ boolean performDexOpt(String packageName); /** * Update status of external media on the package manager to scan and * install packages installed on the external media. Like say the * MountService uses this to call into the package manager to update * status of sdcard. */ void updateExternalMediaStatus(boolean mounted, boolean reportStatus); String nextPackageToClean(String lastPackage); void movePackage(String packageName, IPackageMoveObserver observer, int flags); boolean addPermissionAsync(in PermissionInfo info); boolean setInstallLocation(int loc); int getInstallLocation(); }
由哪个类来实现上面这个框架方法呢?我通过找关键字IPackageManager.Stub找到了class PackageManagerService extends IPackageManager.Stub,下面我们就需要来实现这个抽象方法了:
public AppVoiceEntryInfo getAppVoiceEntryInfo(String packageName, String xmlName,String bnfName){ AppVoiceEntryInfo appVoiceEntryInfo = new AppVoiceEntryInfo(); appVoiceEntryInfo.speechContent = voiceContent; Log.d(TAG,"getAppVoiceEntryInfo method appVoiceEntryInfo.speechContent : " + appVoiceEntryInfo.speechContent); return appVoiceEntryInfo; }
public static String voiceContent = null;
这个变量的赋值:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanMode) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; String scanPath = scanFile.getPath(); parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser(scanPath); pp.setSeparateProcesses(mSeparateProcesses); final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags); voiceContent = pp.voiceContent; Log.d(TAG,"Voice Contect : " + voiceContent);
上面pp.voiceContent;的值是怎么来的呢?接着看PackageParser中的实现:
public static String voiceContent = null; public String convertStreamToString(InputStream is) { /* * To convert the InputStream to String we use the BufferedReader.readLine() * method. We iterate until the BufferedReader return null which means * there's no more data to read. Each line will appended to a StringBuilder * and returned as String. */ BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } public Package parsePackage(File sourceFile, String destCodePath, DisplayMetrics metrics, int flags) { mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = sourceFile.getPath(); if (!sourceFile.isFile()) { Log.w(TAG, "Skipping dir: " + mArchiveSourcePath); mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if (!isPackageFilename(sourceFile.getName()) && (flags&PARSE_MUST_BE_APK) != 0) { if ((flags&PARSE_IS_SYSTEM) == 0) { // We expect to have non-.apk files in the system dir, // so don't warn about them. Log.w(TAG, "Skipping non-package file: " + mArchiveSourcePath); } mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if ((flags&PARSE_CHATTY) != 0 && Config.LOGD) Log.d( TAG, "Scanning package: " + mArchiveSourcePath); XmlResourceParser parser = null; AssetManager assmgr = null; InputStream inputStream = null; boolean assetError = true; try { assmgr = new AssetManager(); int cookie = assmgr.addAssetPath(mArchiveSourcePath); if(cookie != 0) { Log.d(TAG, "sourceFile : " + sourceFile + " destCodePath : " + destCodePath + " flags : " + flags + " cookie : " + cookie + " ArchiveSourcePath : " + mArchiveSourcePath); parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); try{ inputStream = assmgr.open("voice_recognition.xml"); }catch(Exception e){ Log.d(TAG,"" + e); } if(inputStream != null){ Log.d(TAG,"============================= success ==========================" ); voiceContent = convertStreamToString(inputStream); Log.d(TAG,"Voice Contect : " + voiceContent); }
看到上面你应该都明白了吧,呵呵,我也该睡觉了明天还要上班,我电脑太慢, 在Eclipse里面每写个东西就卡死要等一会,上面是个Demo只是为了说明意思,代码不值钱,值钱的是想法,想法哪里来靠看源码啊。
有关过程中的停顿
Checking API: checkapi-last Checking API: checkapi-current (unknown): error 3: Added class AppVoiceEntryInfo to package android.content.pm (unknown): error 4: Added public method android.content.pm.PackageManager.getAppVoiceEntryInfo ****************************** You have tried to change the API from what has been previously approved. To make these errors go away, you have two choices: 1) You can add "@hide" javadoc comments to the methods, etc. listed in the errors above. 2) You can update current.xml by executing the following command: make update-api To submit the revised current.xml to the main Android repository, you will need approval. ****************************** make: *** [out/target/common/obj/PACKAGING/checkapi-current-timestamp] 错误 38
上面提示当你修改了框架代码你更改了api需要执行命令#make update-api