首页 文章 精选 留言 我的

精选列表

搜索[网站开发],共10000篇文章
优秀的个人博客,低调大师

Android开发实践:在任意目录执行NDK编译

本文暂时不讲述如何用NDK编译第三方开源代码库,而是先以简单的例子讲述如何在任意目录把自己写的C代码编译成Android可使用的静态库/动态库。 1. 准备环境 首先,你得安装了Android的NDK编译工具,假设你的NDK的根目录在 /opt/android/ndk . 当然,最好你能在环境变量里配置一下路径,否则使用ndk-build命令的时候,都得加上路径的前缀了。 修改 ~/.bashrc 1 2 export NDK_HOME= /opt/android/ndk export PATH=$NDK_HOME:$PATH 然后执行: 1 $ source ~/.bashrc 2. 编写 .c 文件 假设你在 ~/math 目录下编写了一个 math.c 文件,内容如下: 1 2 3 4 5 #include<stdio.h> int add( int a, int b){ return a+b; } OK,后面我们就准备在 ~/math 目录下,将这个.c文件编译为Android可使用的静态库/动态库了。 3. 编译成动态库 编写 Android.mk 文件,内容如下: 1 2 3 4 5 6 LOCAL_PATH:=$(callmy- dir ) include$(CLEAR_VARS) LOCAL_MODULE:=dmath LOCAL_SRC_FILES:=math.c include$(BUILD_SHARED_LIBRARY) 在 ~/math 目录下,执行 ndk-build 命令,参数如下: 1 $ndk-buildNDK_PROJECT_PATH=.APP_BUILD_SCRIPT=. /Android .mk 根据前一篇文章,我们可以知道,NDK_PROJECT_PATH 指定了需要编译的代码的工程目录,这里给出的是当前目录,APP_BUILD_SCRIPT给出的是Android makefile文件的路径,当然,如果你还有 Application.mk 文件的话,则可以添加 NDK_APP_APPLICATION_MK=./Application.mk 执行完ndk-build命令后,你会发现当前目录下,生成了 obj 和 libs 文件夹,这样,你的libdmath.so动态库就已经制作完成了,在 libs/armeabi 目录下。 4. 编译为静态库 编译为静态库,与编译为动态库唯一的区别就是 Android.mk 文件的写法不同,另外,如果要编译为静态库,则必须有其他的代码引用该静态库代码,ndk-build才会真正执行,否则无法成功生成静态库,这里,我们编写一个 Android.mk ,将 math.c 同时编译成静态库和动态库。 1 2 3 4 5 6 7 8 9 10 11 12 LOCAL_PATH:=$(callmy- dir ) include$(CLEAR_VARS) LOCAL_MODULE:=smath LOCAL_SRC_FILES:=math.c include$(BUILD_STATIC_LIBRARY) include$(CLEAR_VARS) LOCAL_MODULE:=dmath LOCAL_STATIC_LIBRARIES:=smath LOCAL_SRC_FILES:=math.c include$(BUILD_SHARED_LIBRARY) 同样,在 ~/math 目录下执行: 1 $ndk-buildNDK_PROJECT_PATH=.APP_BUILD_SCRIPT=. /Android .mk 你会在生成的 libs/armeabi 目录下看到动态库libdmath.so,在 obj/local/armeabi 目录下看到静态库 libsmath.a 关于在任意目录将c/c++代码编译为Android可使用的静态库/动态库就介绍到这里了,了解了这个编译过程,对你将来用ndk编译第三方库会很有帮助,另外,如果希望编译为C/C++应用程序在Android机器上运行的话,只需要在代码中加一个main函数,修改Android.mk中的最后一行为 include $(BUILD_EXECUTABLE) 即可。 本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1428354,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发实践:WIFI连接功能的封装

与WIFI扫描类似,WIFI的连接同样是一个耗时的过程,所以需要放到线程中执行,通过回调来通知调用者连接结果。该回调接口的定义如下: 1 2 3 public interface WifiConnectListener{ public void OnWifiConnectCompleted( boolean isConnected); } 从Android的WIFI Setting可以看出,一般添加一个新的WIFI连接,需要给出三个信息,一个是WIFI的SSID,一个是WIFI的密码,另一个是WIFI的加密类型,不同的加密方式,连接时程序中的配置是不同的,这里定义一个枚举,给出四种常见的加密类型: 1 2 3 public enum SecurityMode{ OPEN,WEP,WPA,WPA2 } Android的WIFI连接过程,总体上分为三步,第一步,添加网络配置,第二步,根据网络配置连接WIFI,第三步,监听系统的WIFI连接状态消息。下面就直接给出示例代码,关键的地方都在代码中注释了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 package com.example.testwifi; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; public class WifiConnector{ private static final int WIFI_CONNECT_TIMEOUT= 20 ; //连接WIFI的超时时间 private ContextmContext; private WifiManagermWifiManager; private LockmLock; private ConditionmCondition; private WiFiConncetReceivermWifiConnectReceiver; private WifiConnectListenermWifiConnectListener; private boolean mIsConnnected= false ; private int mNetworkID=- 1 ; //网络加密模式 public enum SecurityMode{ OPEN,WEP,WPA,WPA2 } //通知连接结果的监听接口 public interface WifiConnectListener{ public void OnWifiConnectCompleted( boolean isConnected); } public WifiConnector(Contextcontext,WifiConnectListenerlistener){ mContext=context; mLock= new ReentrantLock(); mCondition=mLock.newCondition(); mWifiManager=(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); mWifiConnectReceiver= new WiFiConncetReceiver(); mWifiConnectListener=listener; } public void connect( final Stringssid, final Stringpassword, final SecurityModemode){ new Thread( new Runnable(){ @Override public void run(){ //如果WIFI没有打开,则打开WIFI if (!mWifiManager.isWifiEnabled()){ mWifiManager.setWifiEnabled( true ); } //注册连接结果监听对象 mContext.registerReceiver(mWifiConnectReceiver, new IntentFilter(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)); //连接指定SSID if (!onConnect(ssid,password,mode)){ mWifiConnectListener.OnWifiConnectCompleted( false ); } else { mWifiConnectListener.OnWifiConnectCompleted( true ); } //删除注册的监听类对象 mContext.unregisterReceiver(mWifiConnectReceiver); } }).start(); } protected boolean onConnect(Stringssid,Stringpassword,SecurityModemode){ //添加新的网络配置 WifiConfigurationcfg= new WifiConfiguration(); cfg.SSID= "\"" +ssid+ "\"" ; if (password!= null &&! "" .equals(password)){ //这里比较关键,如果是WEP加密方式的网络,密码需要放到cfg.wepKeys[0]里面 if (mode==SecurityMode.WEP){ cfg.wepKeys[ 0 ]= "\"" +password+ "\"" ; cfg.wepTxKeyIndex= 0 ; } else { cfg.preSharedKey= "\"" +password+ "\"" ; } } cfg.status=WifiConfiguration.Status.ENABLED; //添加网络配置 mNetworkID=mWifiManager.addNetwork(cfg); mLock.lock(); mIsConnnected= false ; //连接该网络 if (!mWifiManager.enableNetwork(mNetworkID, true )){ mLock.unlock(); return false ; } try { //等待连接结果 mCondition.await(WIFI_CONNECT_TIMEOUT,TimeUnit.SECONDS); } catch (InterruptedExceptione){ e.printStackTrace(); } mLock.unlock(); return mIsConnnected; } //监听系统的WIFI连接消息 protected class WiFiConncetReceiver extends BroadcastReceiver{ @Override public void onReceive(Contextcontext,Intentintent){ if (!WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(intent.getAction())){ return ; } mLock.lock(); WifiInfoinfo=mWifiManager.getConnectionInfo(); if (info.getNetworkId()==mNetworkID&&info.getSupplicantState()==SupplicantState.COMPLETED){ mIsConnnected= true ; mCondition.signalAll(); } mLock.unlock(); } } } 与WIFI扫描的封装代码类似,这里也用到了Lock和Condition,就是为了阻塞地等待WIFI连接的结果,保证正确的registerReceiver和unregisterReceiver网络连接状态监听对象,同时,设置了WIFI连接超时,防止由于WIFI模块的问题导致界面收不到回调而长时间“卡死”。 另外,AndroidManifest.xml文件中记得添加权限支持哦: 1 2 3 <uses-permissionandroid:name= "android.permission.CHANGE_WIFI_STATE" ></uses-permission> <uses-permissionandroid:name= "android.permission.ACCESS_WIFI_STATE" ></uses-permission> <uses-permissionandroid:name= "android.permission.ACCESS_NETWORK_STATE" ></uses-permission> 本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1410080,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发实践:多级列表的封装与应用

Android中多级列表可以使用ExpandableListView和SimpleExpandableListAdapter配合来实现,但是,SimpleExpandableListAdapter用起来挺麻烦的,不易理解,而且扩展性也不好,因此,自定义BaseExpandableListAdapter类的子类以及封装相关的操作,用起来会更加直观和方便,我把我设计的封装贴出来供新手参考吧。 首先上效果图,如图所示: 1. 首先设计多级列表的标题类 就像文件和文件夹可以统一地用File类来抽象一样,多级列表的一级标题和二级标题其实也可以用同一个基类来抽象,因此,我设计了一个基类和两个子类,GroupList,GroupListChild 和 GroupListParent,其实现如下所示: (1) GroupList 多级列表标题的抽象基类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public abstract class GroupList{ private final StringmTitle; public GroupList(Stringtitle){ mTitle=title; } public StringgetTitle(){ return mTitle; } public abstract List<GroupList>getChild(); public abstract int getResource(); public abstract void buildView(Viewv); } (2) GroupListChild 多级列表二级标题子类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class GroupListChild extends GroupList{ public GroupListChild(Stringtitle){ super (title); } @Override public int getResource(){ return R.layout.grouplist_child; } @Override public List<GroupList>getChild(){ return null ; } @Override public void buildView(Viewv){ TextViewtextView=(TextView)v.findViewById(R.id.GroupListChild); textView.setText(getTitle()); } } (3) GroupListParent 多级列表一级标题子类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class GroupListParent extends GroupList{ private List<GroupList>mPopListChilds; public GroupListParent(Stringtitle,List<GroupList>childs){ super (title); mPopListChilds=childs; } @Override public int getResource(){ return R.layout.grouplist_parent; } @Override public List<GroupList>getChild(){ return mPopListChilds; } @Override public void buildView(Viewv){ TextViewtextView=(TextView)v.findViewById(R.id.GroupListParent); textView.setText(getTitle()); } } 2. 设计BaseExpandableListAdapter的子类 我设计的子类是一种通用的Adapter子类,类的实现中并不包含具体的Layout实现,所有的Layout都由GroupList的getResource和buildView来负责,因此,可以非常灵活地修改Layout的具体实现,而不用修改Adapter的代码。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 public class GroupListAdapter extends BaseExpandableListAdapter{ private ContextmContext; private List<GroupList>mGroups; public GroupListAdapter(Contextcontext,List<GroupList>groups){ this .mContext=context; this .mGroups=groups; } @Override public ObjectgetChild( int groupPosition, int childPosition){ List<GroupList>chList=mGroups.get(groupPosition).getChild(); if (chList== null ){ return null ; } return chList.get(childPosition); } @Override public long getChildId( int groupPosition, int childPosition){ return childPosition; } @Override public ViewgetChildView( int groupPosition, int childPosition, boolean isLastChild,Viewview,ViewGroupparent){ GroupListchild=(GroupList)getChild(groupPosition,childPosition); if (child== null ){ return null ; } if (view== null ){ LayoutInflaterinflater=(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view=(RelativeLayout)inflater.inflate(child.getResource(), null ); } child.buildView(view); return view; } @Override public int getChildrenCount( int groupPosition){ List<GroupList>chList=mGroups.get(groupPosition).getChild(); if (chList== null ){ return 0 ; } return chList.size(); } @Override public ObjectgetGroup( int groupPosition){ return mGroups.get(groupPosition); } @Override public int getGroupCount(){ return mGroups.size(); } @Override public long getGroupId( int groupPosition){ return groupPosition; } @Override public ViewgetGroupView( int groupPosition, boolean isLastChild,Viewview,ViewGroupparent){ GroupListgroup=(GroupList)getGroup(groupPosition); if (view== null ){ LayoutInflaterinflater=(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view=(RelativeLayout)inflater.inflate(group.getResource(), null ); } group.buildView(view); return view; } @Override public boolean hasStableIds(){ return true ; } @Override public boolean isChildSelectable( int arg0, int arg1){ return true ; } } 3. 应用代码 为了简化,我就直接在MainActivity中使用上述封装的类来完成多级列表的功能演示,示例如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class MainActivity extends Activity implements OnGroupClickListener,OnChildClickListener{ private ExpandableListViewmlistView; private GroupListAdaptermAdapter; private static final String[]mParentMenu={ "Book" , "Video" , "Audio" }; private static final String[][]mChildMenu={ { "book1" , "book2" , "book3" , "book4" }, { "video1" , "video2" }, { "audio1" , "audio2" , "audio3" , "audio4" } }; @Override protected void onCreate(BundlesavedInstanceState){ super .onCreate(savedInstanceState); mlistView= new ExpandableListView( this ); mlistView.setOnGroupClickListener( this ); mlistView.setOnChildClickListener( this ); List<GroupList>groups= new ArrayList<GroupList>(); for ( int i= 0 ;i<mParentMenu.length;i++){ List<GroupList>childs= new ArrayList<GroupList>(); for ( int j= 0 ;j<mChildMenu[i].length;j++){ childs.add( new GroupListChild(mChildMenu[i][j])); } groups.add( new GroupListParent(mParentMenu[i],childs)); } mAdapter= new GroupListAdapter( this ,groups); mlistView.setAdapter(mAdapter); setContentView(mlistView); } @Override public boolean onChildClick(ExpandableListViewparent,Viewv, int groupPosition, int childPosition, long id){ return false ; } @Override public boolean onGroupClick(ExpandableListViewparent,Viewv, int groupPosition, long id){ return false ; } } 4. 相关的xml文件 (1) grouplist_child.xml 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "match_parent" android:orientation = "horizontal" > < TextView android:id = "@+id/GroupListChild" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textColor = "#FFFF0000" android:layout_margin = "10dp" android:layout_centerInParent = "true" /> </ RelativeLayout > (2) grouplist_parent.xml 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "match_parent" android:orientation = "horizontal" > < TextView android:id = "@+id/GroupListParent" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textColor = "@android:color/black" android:textStyle = "bold" android:layout_margin = "10dp" android:layout_centerInParent = "true" /> </ RelativeLayout > 本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1289642,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android内核开发:浅析APK的安装过程

1. 一般Windows应用程序的安装过程分为如下几步: (1) 解压exe文件到系统的临时目录 这个临时目录通常是C:\Windows\Temp ,当然,并不是所有的程序的安装都需要先解压到临时目录。 (2) 拷贝核心文件到指定的程序目录 一般一个应用程序都会包含很多文件,如二进制文件、图标、静态库、动态库、配置文件、其他资源文件等等。这个过程就是把这些必须的文件拷贝到目标目录,如:D:\Software\软件名\,当然,有的软件还会把一些重要的或者公共的dll文件拷贝到系统的目录下(C:\Windows\),如一些驱动文件、系统级别的dll文件、公共共享文件等等。 (3) 写注册表 很多操作需要写系统的注册表,如安装属性的配置、注册服务程序、设置文件关联、添加右键菜单、注册dll文件、在控制面板的添加/删除程序中注册自己等等。 (4) 添加到开始菜单以及桌面快捷方式 这个不用过多解释,虽然我们可以每次都进入到程序安装目录点击exe来启动程序,但是添加程序到开始菜单以及桌面快捷方式可以更加方便地为用户提供访问程序的入口。 2. 然后,我们再来看看Android系统上APK是怎么安装的? (1) 拷贝apk文件到指定目录 在Android系统中,apk安装文件是会被保存起来的,默认情况下,用户安装的apk首先会被拷贝到 /data/app 目录下。 /data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件,而系统出厂的apk文件则被放到了 /system 分区下,包括/system/app,/system/vendor/app,以及/system/priv-app 等等,该分区只有Root权限的用户才能访问,这也就是为什么在没有Root手机之前,我们无法删除系统出厂的app的原因了。 (2) 解压apk,拷贝文件,创建应用的数据目录 为了加快app的启动速度,apk在安装的时候,会首先将app的可执行文件(dex)拷贝到 /data/dalvik-cache 目录,缓存起来。 然后,在/data/data/目录下创建应用程序的数据目录(以应用的包名命名),存放应用的相关数据,如数据库、xml文件、cache、二进制的so动态库等等。 (3) 解析apk的AndroidManifinest.xml文件 Android系统中,也有一个类似注册表的东西,用来记录当前所有安装的应用的基本信息,每次系统安装或者卸载了任何apk文件,都会更新这个文件。这个文件位于如下目录: /data/system/packages.xml 系统在安装apk的过程中,会解析apk的AndroidManifinest.xml文件,提取出这个apk的重要信息写入到packages.xml文件中,这些信息包括:权限、应用包名、APK的安装位置、版本、userID等等。 由此,我们就知道了为啥一些应用市场和软件管理类的app能够很清楚地知道当前手机所安装的所有的app,以及这些app的详细信息了。 (4) 其他操作 与windows应用安装类似,部分apk的安装也会向Launcher应用申请添加创建快捷方式。 本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1669525,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册