Android 如何自定义共享库
Android创建和使用数据库详细指南
Android创建和使用数据库详细指南(1)摘要:每个应用程序都要使用数据,Android应用程序也不例外,Android使用开源的、与操作系统无关的SQL数据库--SQLite,本文介绍的就是如何为你的Android应用程序创建和操作SQLite数据库。
数据库支持每个应用程序无论大小的生命线,除非你的应用程序只处理简单的数据,那么就需要一个数据库系统存储你的结构化数据,Android使用SQLite数据库,它是一个开源的、支持多操作系统的SQL数据库,在许多领域广泛使用,如Mozilla FireFox就是使用SQLite 来存储配置数据的,iPhone也是使用SQLite来存储数据的。
在Android中,你为某个应用程序创建的数据库,只有它可以访问,其它应用程序是不能访问的,数据库位于Android设备/data/data//databases文件夹中,在这篇文章中,你将会学习到如何在Android中创建和使用数据库。
1SQLite数据库使用Eclipse创建一个Android项目,取名为Database,如图1所示:图1 数据库-使用Eclipse创建你的Android新项目2创建DBAdapter辅助类接下来创建一个数据库,取名为bookstitles,字段如图2所示。
图2 数据库字段在DBAdapter.java文件中,定义清单1中的常量。
清单1 定义DBAdapter.java文件中的常量package net.learn2develop.Database;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.SQLException;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import android.util.Log;public class DBAdapter{public static final String KEY_ROWID = "_id";public static final String KEY_ISBN = "isbn";public static final String KEY_TITLE = "title";public static final String KEY_PUBLISHER = "publisher";private static final String TAG = "DBAdapter";private static final String DATABASE_NAME = "books";private static final String DATABASE_TABLE = "titles";private static final int DATABASE_VERSION =1;private static final String DATABASE_CREATE ="create table titles (_id integer primary key autoincrement, "+ "isbn text not null, title text not null, "+ "publisher text not null);";private final Context context;}DATABASE_CREATE常量包括创建titles表的SQL语句。
Android xml文件存储 SharedPreferences共享参数
Android存储方式一、xml 文件方式SharedPreferences 共享参数1.界面约束布局layout包下activity_main.xml<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E6E6E6"tools:context=".MainActivity"><Buttonandroid:id="@+id/button_ok"android:layout_width="280dp"android:layout_height="50dp"android:layout_marginEnd="8dp"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:layout_marginStart="8dp"android:layout_marginTop="248dp"android:background="@drawable/shape"android:text="登陆"android:textColor="#ffffff"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/username"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@color/white"tools:ignore="MissingConstraints" /><EditTextandroid:id="@+id/editText1"android:layout_width="280dp"android:layout_height="wrap_content"android:layout_marginEnd="8dp"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:layout_marginStart="8dp"android:layout_marginTop="16dp"android:ems="10"android:hint="密码"android:textColorHint="#ffffff"android:inputType="textPassword"android:textColor="#ffffff"android:selectAllOnFocus="false"android:singleLine="false"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/editText2" /><TextViewandroid:id="@+id/password"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@color/white"tools:ignore="MissingConstraints" /><EditTextandroid:id="@+id/editText2"android:layout_width="280dp"android:layout_height="wrap_content"android:layout_marginEnd="8dp"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:layout_marginStart="8dp"android:layout_marginTop="130dp"android:ems="10"android:hint="手机号/邮箱"android:textColorHint="#ffffff"android:textColor="#ffffff"android:inputType="textPersonName"android:textSize="18sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.503"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>2.创建MainActivitypackage com.example.cunchufangshi;import androidx.appcompat.app.AppCompatActivity;import android.content.Context;import android.os.Bundle;import android.text.TextUtils;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;import java.util.Map;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private EditText editTex1;private EditText editTex2;private Button button_ok;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(yout.activity_main);initView();// Map<String,String> userInfo=FileSave.getUserInfo(this);Map<String,String> userInfo=SPSave.getUserinfo(this);if (userInfo!=null){editTex1.setText(userInfo.get("username"));editTex2.setText(userInfo.get("password"));}}private void initView() {editTex1=findViewById(R.id.editText1);editTex2=findViewById(R.id.editText2);button_ok=findViewById(R.id.button_ok);//触发button_ok.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.button_ok:String username=editTex1.getText().toString().trim();//获取账号String password=editTex2.getText().toString().trim();//获取密码//判断账号密码是否为空//账号为空if (TextUtils.isEmpty(username)){Toast.makeText(this, "账号为空", Toast.LENGTH_LONG).show();return;}//密码为空if (TextUtils.isEmpty(password)){Toast.makeText(this, "密码为空", Toast.LENGTH_LONG).show();return;}//若是已有用户Toast.makeText(this, "登录成功", Toast.LENGTH_LONG).show();//若是第一次登录//Boolean isFileSaveSucess=FileSave.saveUserInfo(this, username, password);//Boolean isFileSaveSucess=SPSave.saveUserinfo(this, username, password);if (isFileSaveSucess){Toast.makeText(this, "信息保存成功", Toast.LENGTH_LONG).show(); }else {Toast.makeText(this, "信息保存失败", Toast.LENGTH_LONG).show(); }}}}3.创建SPSavepackage com.example.cunchufangshi;import android.content.Context;import android.content.SharedPreferences;import java.util.HashMap;import java.util.Map;// xml文件方式数据存储 SharedPreferencespublic class SPSave {/*** 把账号和密码保存到data.xml文件中* @param context* @param username* @param password* @return*/public static boolean saveUserinfo(Context context,String username,String password){SharedPreferences sp=context.getSharedPreferences("data",Context.MODE_PRIVATE);SharedPreferences.Editor editor=sp.edit();//获取Editor对象editor.putString("username",username);editor.putString("password",password);mit();//提交return true;}/*** 从data.xml文件中将账号、密码信息获取出来* @param context* @return*/public static Map<String,String> getUserinfo(Context context){ SharedPreferences sp=context.getSharedPreferences("data", Context.MODE_PRIVATE);String usernanme=sp.getString("username",null);String password=sp.getString("password",null);Map<String,String> mapuser=new HashMap<String, String>(); mapuser.put("username", usernanme);mapuser.put("password", password);return mapuser;}}。
Android移动开发第8章 数据存储与共享
资源文件
1.访问res/raw目录下的原始格式文件
2.访问res/xml目录下的原始XML格式文件
3
数据库存储
主要内容
1
手动建库
2 代码建库 3 数据操作
手动建库
1.启动和退出sqlite3
输入 adb shell 命令
输入 sqlite3
手动建库
2.建立数据库目录
在/data/data/com.mingrisoft目录下创建子目录databases,可 以使用下面的命令。
数据操作
1.添加操作
insert()方法的基本语法格式如下:
public long insert (String table, String nullColumnHack, ContentValues values)
2.更新操作
update()方法的基本语法格式如下:
update(String table, ContentValues values, String whereClause, String[] whereArgs)
用于返回当前指针的位置
数据操作
4.删除操作
delete()方法的基本语法格式如下:
delete(String table, String whereClause, String[] whereArgs)
数据操作
在Eclipse中创建Android项目,实现向个人理财通的数据库中添加、删除、 更新和查询收入信息。
putBoolean()方法添加布尔型数据、调用putString()方法添加字符串数据、
调用putInt()方法添加整型数据,可以使用下面的代码。
editor.putString("username", username); editor.putBoolean("status", false); editor.putInt("age", 20);
浅析Android之数据共享
浅析Android之数据共享摘要:Android是基于Linux开放性内核的操作系统,是Google公司在2007年11月5日公布的手机操作系统。
Android作为Google移动互联网战略的重要组成部分,将进一步推进“随时随地为每个人提供信息”这一企业目标的实现。
Google的目标是让移动通信不依赖于设备,甚至是平台。
出于这个目的,Android将完善而不是替代Google 长期以来推行的移动发展战略:通过与全球各地的手机制造商和移动运营商成为合作伙伴,开发既实用又有吸引力的移动服务,并推广这些产品。
关键词:保存方式数据共享文件1 Android数据共享数据是应用的核心,但是在Android中,每一个应用都运行在各自的进程中,当一个应用需要访问其他应用的数据时,也就是数据需要在不同的虚拟机之间传递,这样的情况操作起来可能有些困难——在正常情况下,不能读取其他应用的数据。
那么Android是如何实现应用程序之间数据共享的?一个应用程序可以通过一套标准及统一的接口将自己的数据暴露出去,而外界可以通过这一套标准及统一的接口和这个程序里的数据打交道。
2 通过Intent实现数据共享Android为了屏蔽进程的概念,利用不同的组件(Activity、Service)来表示进程之间的通信。
组件间通信的核心机制是Intent,通过Intent 可以开启一个Activity或Service,而不论这个Activity或Service是属于当前应用还是其它应用的。
Intent包含两部分。
2)隐式——需要在AndroidMainifest.xml中注册,一般用于跨进程通信。
new Intent(String action)有了Intent这种基于消息的进程内或进程间通信模型,我们就可以通过Intent去开启一个Service,也可以通过Intent跳转到另一个Activity,不论上面的Service或Activity是在当前进程还是其它进程内的,即不论Service或Activity是当前应用还是其它应用的,通过消息机制都可以进行通信,实现数据共享。
Android:cmake开发指南
Android:cmake开发指南⼀、静态库与动态库构建 (.so)共享库,shared object:节省空间,在运⾏时去连接,如果执⾏机器上没有这些库⽂件就不能执⾏。
(.a)静态库,archive:静态库和程序化为⼀体,不会分开。
通过 ldd命令可以查看⼀个可执⾏程序所依赖的的共享库。
使⽤环境变量LD_LIBRARY_DIRECTORY可以指定共享库位置 1.编译共享库:ADD_LIBRARY(hello SHARED ${SHARED_LIBRARY}) 2.添加静态库:ADD_LIBRARY(hello STATIC ${STATIC_LIBRARY}) 因为默认规则是不能有相同名字的共享库与静态库,所以当⽣成静态库的时候,共享库会被删除,所以需要通过SET_TARGET_PROPERTIES()来解决这个问题,例⼦:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") cmake在构建⼀个target的时候,会删除之前⽣成的target,⼀样是通过设置SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)来达到⽬的。
3.动态库的版本号: 同样是通过SET_TARGET_PROPERTIES()来设置SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1) VERSION:动态库版本 SOVERSION:API版本⼆、静态库与动态库构建常⽤变量和常⽤环境变量 1.变量的引⽤ 使⽤“${}”,在IF中,不需要使⽤这种⽅式,直接使⽤变量名即可。
2.⾃定义变量 使⽤SET(OBJ_NAME xxxx),使⽤时${OBJ_NAME} 3.cmake的常⽤变量:CMAKE_BINARY_DIR,PROJECT_BINARY_DIR,_BINARY_DIR 这三个变量内容⼀致,如果是内部编译,就指的是⼯程的顶级⽬录,如果是外部编译,指的就是⼯程编译发⽣的⽬录。
library如何使用 sharedpreference
library如何使用sharedpreference SharedPreference是Android中一种轻量级的数据存储方式,它可以存储一些简单的键值对数据,比如用户的偏好设置、应用的配置信息等。
SharedPreference的使用非常简单,只需要调用一些简单的API就可以实现数据的存储和读取。
本文将介绍如何使用SharedPreference来存储和读取数据。
一、创建SharedPreference对象在使用SharedPreference之前,需要先创建一个SharedPreference对象。
SharedPreference对象可以通过Context的getSharedPreferences()方法来创建。
该方法有两个参数,第一个参数是SharedPreference的名称,第二个参数是操作模式。
操作模式有两种,MODE_PRIVATE和MODE_MULTI_PROCESS。
MODE_PRIVATE表示只有当前应用可以访问该SharedPreference,MODE_MULTI_PROCESS表示多个进程可以同时访问该SharedPreference。
下面是一个创建SharedPreference对象的示例代码:SharedPreferences sharedPreferences =getSharedPreferences("my_preference", Context.MODE_PRIVATE);二、存储数据存储数据是SharedPreference的主要功能之一。
SharedPreference可以存储一些简单的数据类型,比如字符串、整数、布尔值等。
存储数据可以通过SharedPreferences.Editor对象来实现。
SharedPreferences.Editor对象可以通过SharedPreferences的edit()方法来获取。
存储数据的过程分为以下几步:1. 获取SharedPreferences.Editor对象SharedPreferences.Editor editor = sharedPreferences.edit();2. 存储数据存储数据可以通过SharedPreferences.Editor对象的putXXX()方法来实现,其中XXX表示数据类型,比如putString()表示存储字符串,putInt()表示存储整数,putBoolean()表示存储布尔值等。
Android 使用ContentProvider共享数据
Android 使用ContentProvider共享数据当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。
虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。
而使用ContentProvider共享数据的好处是统一了数据访问方式。
当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:Java代码:Java代码1publicclassPersonContentProviderextendsContentProvider{2publicbooleanonCreate()3publicUriinsert(Uriuri,ContentV aluesvalues)4publicintdelete(Uriuri,Stringselection,String[]selectionArgs)5publicintupdate(Uriuri,ContentV aluesvalues,Stringselection,String[]selectionArgs)6publicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,Strings ortOrder)7publicStringgetType(Uriuri)}第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider ,ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:java代码:XML/HTML代码8<manifest>9<applicationandroid:icon="@drawable/icon"android:label="@string/app_name">10<providerandroid:name=".PersonContentProvider"android:authorities="cn.android.provid er.personprovider"/>11</application>12</manifest>注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为ContentProviderjava代码:Java代码13publicclassPersonContentProviderextendsContentProvider{14//数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头15publicstaticfinalStringPERSONS_TYPE="vnd.android.cursor.dir/person";16//单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头17publicstaticfinalStringPERSONS_ITEM_TYPE="vnd.android.cursor.item/person";18publicstaticfinalStringAUTHORITY="cn.android.provider.personprovider";//主机名19/*自定义匹配码*/20publicstaticfinalintPERSONS=1;21/*自定义匹配码*/22publicstaticfinalintPERSON=2;23publicstaticfinalUriPERSONS_URI=Uri.parse("content://"+AUTHORITY+"/person"); 2425/*这里UriMatcher是用来匹配Uri的类,使用match()方法匹配路径时返回匹配码*/ 26privatestaticfinalUriMatchersMatcher;27static{28sMatcher=newUriMatcher(UriMatcher.NO_MA TCH);//常量UriMatcher.NO_MA TCH表示不匹配任何路径的返回码29//如果match()方法匹配content://cn.android.provider.personprovider/person路径,返回匹配码为PERSONS30sMatcher.addURI(AUTHORITY,"person",PERSONS);31//如果match()方法匹配content://cn.android.provider.personprovider/person/230路径,返回匹配码为PERSON32sMatcher.addURI(AUTHORITY,"person/#",PERSON);33}34privateDatabaseHelperdatabaseHelper;35@Override36publicbooleanonCreate(){37databaseHelper=newDatabaseHelper(this.getContext());38returntrue;39}4041@Override42publicUriinsert(Uriuri,ContentV aluesvalues){43SQLiteDatabasedb=databaseHelper.getWritableDatabase();44if(sMatcher.match(uri)!=PERSONS){45thrownewIllegalArgumentException("UnknownURI"+uri);46}47longrowId=db.insert("person","personid",values);//往person表添加一条记录48db.close();49if(rowId>0){//如果添加成功50//ContentUris是contentURI的一个辅助类。
android共享内存(ShareMemory)的实现
android共享内存(ShareMemory)的实现Android 几种进程通信方式跨进程通信要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。
然后,返回值将沿相反方向传输回来。
Android 为我们提供了以下几种进程通信机制(供开发者使用的进程通信 API)对应的文章链接如下:•文件•AIDL (基于 Binder)•Binder•Messenger (基于 Binder)•ContentProvider (基于 Binder)•Socket在上述通信机制的基础上,我们只需集中精力定义和实现 RPC 编程接口即可。
如何选择这几种通信方式这里再对比总结一下:•只有允许不同应用的客户端用 IPC 方式调用远程方法,并且想要在服务中处理多线程时,才有必要使用 AIDL•如果需要调用远程方法,但不需要处理并发 IPC,就应该通过实现一个Binder 创建接口•如果您想执行 IPC,但只是传递数据,不涉及方法调用,也不需要高并发,就使用 Messenger 来实现接口•如果需要处理一对多的进程间数据共享(主要是数据的 CRUD),就使用ContentProvider•如果要实现一对多的并发实时通信,就使用 Socketimage.pngIPC分析:android IPC的核心方式是binder,但是android binder的传输限制1M(被很多进程共享),在较大数据交换一般通过文件,但是效率很低,因此介绍下新的内存共享方式: ShareMemory具体实现:通过binder把MemoryFile的ParcelFileDescriptor 传到Service;在服务端通过ParcelFileDescriptor 读取共享内存数据;•客户端 LocalClient.java 通过MemoryFile获取ParcelFileDescriptor,通过Binder把ParcelFileDescriptor(int类型)传递到服务端•服务端 RemoteService 获取到ParcelFileDescriptor 之后,有两种方式第一种:通过FileInputStream 读取ParcelFileDescriptor 的FD,此种方式的问题在于,每次读取之后FD的游标都在文件最后(也就是说第二次读取结果是不低的,必须重置FD的游标) 第二种:就是通过反射,直接ParcelFileDescriptor构建MemoryFile,然后读取,此种方式问题在于26和27实现的不同,代码如下:Android P(9.0)反射限制: 上述反射的方式在android P上被限制(android 9.0禁止通过放射调用系统的的非公开方法),此路不同(If they cut off one head, two more shall take it's place... Hail Hydra.),还有千万条路•ShareMemory android O(8.0)之后增加新的共享内存方式,SharedMemory.java 此类继承Parcelable,可以作为IPC通讯传输的数据;•ClassLoader 多态:此方式并非真正的多态,而是根据ClassLoader类的加载顺序,在应用中构建一个和系统类同样包名的类(方法也同名,可以不做实现),编译时使用的应用中的类,运行时加载的是系统中的类,从而实现伪多态;GitHub:ShareMemory优点:binder 限制(binder的android上的限制1M,而且是被多个进程共享的); binder 在android进程中经过一次内存copy,内存共享通过mmap,0次copy效率更高;。
android sharedmemory用法
android sharedmemory用法全文共四篇示例,供读者参考第一篇示例:Android SharedMemory是一种用于在多个进程之间共享数据的机制。
它允许不同应用程序之间共享大块内存,这对于需要高性能数据交换的应用程序非常有用,比如多媒体应用程序或游戏。
在Android系统中,每个进程都有自己的独立地址空间,因此默认情况下进程之间不能直接共享内存。
但是Android SharedMemory 提供了一种方法让不同进程之间可以共享内存块。
这种共享内存块创建的共享内存区域可以由不同进程映射到自己的地址空间中,从而实现数据共享。
SharedMemory的用法非常简单,首先需要创建一个SharedMemory对象,然后使用该对象创建一个共享内存区域,并将数据写入其中。
接着,其他进程可以通过SharedMemory对象来访问共享内存区域,即可以将这个内存区域映射到自己的地址空间,并读取其中的数据。
在Android中,可以使用SharedMemory API来实现SharedMemory的功能。
下面是一个基本的SharedMemory用法示例:1. 创建SharedMemory对象SharedMemory sharedMemory =SharedMemory.create("shared_memory_name", 1024);这行代码创建了一个名为"shared_memory_name",大小为1024字节的共享内存区域。
2. 写入数据这段代码将"Hello, SharedMemory!"这个字符串写入了共享内存区域中。
3. 读取数据SharedMemory sharedMemory =SharedMemory.create("shared_memory_name", 1024);ByteBuffer byteBuffer = sharedMemory.mapReadOnly();byte[] data = new byte[1024];byteBuffer.get(data);通过上面的示例,我们可以看到SharedMemory的基本用法。
如何实现Android应用程序之间数据共享的
如何实现Android应用程序之间数据共享的一个应用程序可以将自己的数据完全暴露出去,外界更本看不到,也不用看到这个应用程序暴露的数据是如何存储的,或者是使用数据库还是使用文件,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和这个程序里的数据打交道,例如:添加(insert)、删除(delete)、查询(query)、修改(update),当然需要一定的权限才可以。
如何将应用程序的数据暴露出去? Android提供了ContentProvider,一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,而且Content providers 是以类似数据库中表的方式将数据暴露。
Content providers存储和检索数据,通过它可以让所有的应用程序访问到,这也是应用程序之间唯一共享数据的方法。
要想使应用程序的数据公开化,可通过2种方法:创建一个属于你自己的Content provider或者将你的数据添加到一个已经存在的Content provider中,前提是有相同数据类型并且有写入Content provider的权限。
如何通过一套标准及统一的接口获取其他应用程序暴露的数据?Android提供了ContentResolver,外界的程序可以通过ContentResolver接口访问ContentProvider提供的数据。
当前篇主要说明,如何获取其它应用程序共享的数据,比如获取Android 手机电话薄中的信息。
什么是URI?在学习如何获取ContentResolver前,有个名词是必须了解的:URI。
URI是网络资源的定义,在Android中赋予其更广阔的含义,先看个例子,如下:将其分为A,B,C,D 4个部分:A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;B:URI的标识,它定义了是哪个Content Provider提供这些数据。
Android通过ViewModel保存数据实现多页面的数据共享功能
Android通过ViewModel保存数据实现多页⾯的数据共享功能通过ViewModel实现的数据共享符合Android的MVC设计模式,将数据独⽴出来实现的Demo1、主页⾯通过SeekBar 来改变数字的值2、点击进⼊就进⼊第⼆个界⾯,但是数据还是共享的3、随便加两个数字上去,再次切换4、发现数据还是共享的下⾯是具体实现步骤:1、建⽴两个Fragment(使⽤了Binding 和 Navigation)⼀点要添加Binding 和 Navigation 不然做不了2、建⽴⼀个继承于ViewModel的类3、分别在两个Fragment的代码中使⽤继承于ViewModel的那个类,就可以实现数据共享下⾯是具体代码:1、继承于ViewModel的类package com.example.naviation01;import androidx.lifecycle.MutableLiveData;import androidx.lifecycle.ViewModel;public class MyViewMode extends ViewModel {private MutableLiveData<Integer> number;public MutableLiveData<Integer> getNumber(){if(this.number == null){this.number = new MutableLiveData<>();this.number.setValue(0);}return this.number;}public void add(int x){this.number.setValue(this.number.getValue()+x);if(this.number.getValue() < 0){this.number.setValue(0);}}}2、Fragment 主页package com.example.naviation01;import android.os.Bundle;import androidx.databinding.DataBindingUtil;import androidx.fragment.app.Fragment;import androidx.fragment.app.FragmentController;import androidx.lifecycle.ViewModel;import androidx.lifecycle.ViewModelProvider;import androidx.lifecycle.ViewModelProviders;import androidx.navigation.NavController;import androidx.navigation.Navigation;import youtInflater;import android.view.View;import android.view.ViewGroup;import android.widget.SeekBar;import com.example.naviation01.databinding.FragmentHomeBinding;/*** A simple {@link Fragment} subclass.*/public class HomeFragment extends Fragment {public HomeFragment() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentfinal MyViewMode myViewMode;myViewMode = ViewModelProviders.of(getActivity()).get(MyViewMode.class);FragmentHomeBinding binding;binding = DataBindingUtil.inflate(inflater,yout.fragment_home,container,false);binding.setData(myViewMode);binding.setLifecycleOwner(getActivity());binding.seekBar.setProgress(myViewMode.getNumber().getValue());binding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {myViewMode.getNumber().setValue(progress);}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});binding.button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {NavController controller = Navigation.findNavController(v);controller.navigate(R.id.action_homeFragment_to_detailFragment);}});return binding.getRoot();//return inflater.inflate(yout.fragment_home, container, false);}}xml<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"><data><variablename="data"type="com.example.naviation01.MyViewMode" /></data><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".HomeFragment"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"android:text="@{String.valueOf(data.number)}"android:textSize="30sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.255" /><SeekBarandroid:id="@+id/seekBar"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.456" /><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"android:text="@string/function01"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.679" /></androidx.constraintlayout.widget.ConstraintLayout></FrameLayout></layout>3、Fragment 副页package com.example.naviation01;import android.os.Bundle;import androidx.databinding.DataBindingUtil;import androidx.fragment.app.Fragment;import androidx.lifecycle.ViewModelProviders;import androidx.navigation.NavController;import androidx.navigation.Navigation;import youtInflater;import android.view.View;import android.view.ViewGroup;import com.example.naviation01.databinding.FragmentDetailBinding; /*** A simple {@link Fragment} subclass.*/public class DetailFragment extends Fragment {public DetailFragment() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentMyViewMode myViewMode;myViewMode = ViewModelProviders.of(getActivity()).get(MyViewMode.class); FragmentDetailBinding binding;binding = DataBindingUtil.inflate(inflater,yout.fragment_detail,container,false); binding.setDate(myViewMode);binding.setLifecycleOwner(getActivity());binding.button4.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {NavController controller = Navigation.findNavController(v);controller.navigate(R.id.action_detailFragment_to_homeFragment);}});return binding.getRoot();//return inflater.inflate(yout.fragment_detail, container, false);}}xml<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"><data><variablename="date"type="com.example.naviation01.MyViewMode" /></data><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"tools:context=".DetailFragment"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/textView3"android:layout_width="131dp"android:layout_height="55dp"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"android:text="@{String.valueOf(date.number)}"android:textSize="30sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.23" /><Buttonandroid:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"android:text="@string/function02"android:onClick="@{()->date.add(1)}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.104"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.499" /><Buttonandroid:id="@+id/button3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"android:text="@string/function03"android:onClick="@{()->date.add(-1)}"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.899"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.499" /><Buttonandroid:id="@+id/button4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"android:text="@string/function04"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.498"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.664" /></androidx.constraintlayout.widget.ConstraintLayout></FrameLayout></layout>4、xml Main_Activity<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><fragmentandroid:id="@+id/fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_marginBottom="8dp"app:defaultNavHost="true"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:navGraph="@navigation/nav_graph" /></androidx.constraintlayout.widget.ConstraintLayout>总结以上所述是⼩编给⼤家介绍的Android通过ViewModel保存数据实现多页⾯的数据共享功能,希望对⼤家有所帮助,如果⼤家有任何疑问请给我留⾔,⼩编会及时回复⼤家的。
Android实用教程 第6章 Android数据存储与共享
SharedPreferences举例
(1)设计页面 用第3章学过的表格布局设计注册页面register.xml,代码。 其中: (a)android:inputType="numberPassword" android:hint="必须全部为数字":文本编辑框输入 内容均显示“.”,初始提示信息为“必须全部为数字”。 (b)<ToggleButton...android:checked="true" />:开关按钮控件属性checked="true"(默认), 显示“男”;属性checked="false",显示“女”。 (c)android:inputType="date":文本显示内容为日期。 (d)<SpinnerSpinner android:id="@+id/mySpinnerDegree"/>:控件选择输入内容,选择项目 “博士、硕士、学士”在findViews()中添加。 (e)android:text="已阅读并接受\n网站服务条款" android:layout_gravity="center|right" androi d:onClick="onCheckBoxClick":提示信息包含“\n”表示换行;上下对中水平右对齐;单击按钮,执行on CheckBoxClick()方法。 (f)style="?android:attr/buttonStyleSmall" android:enabled="false":命令按钮采用buttonStyle Small风格,初始状态不可用。在myCheckBoxAccept控件的单击事件中判断若两次密码相同,使该命令 按钮可用。
9、Android数据存储和共享初级(教程与案例)
(初级篇)
本章目标
• Linux用户权限管理 • 存储一:SharePrefences • 存储二:FILE • 存储三:Sqlite数据库 • SQLite3数据库的操作 SQLiteDatabase/SQLiteDatabaseHelper • SQLite数据库事务和锁
• 在使用时,继承该类,实现onCreate和 onUpgrade两个抽象方法
– onCreate(SQLiteDatabase db) : 当数据库被首次创建时 执行该方法,一般将创建表等初始化操作在该方法中执行 。 – onUpgrade(SQLiteDatabse dv, int oldVersion,int new Version):当打开数据库时传入的版本号与当前的版本号 不同时会调用该方法。
• 写
– 1、Preferences对象的editor函数获得编辑对 – 2、Preferences.Editor mEditor – 3、mEditor.put系列方法写入数据 – 4、mit提交数据到文件
不同―应用程序‖间,若想共享他方的Share Preferences文件,则需要创建 他方包的上下文环境; Content cont= this.createPackageContext(―他方包名‖, Context.CONTEXT_IGNORE_SECURITY); cont.getSharedPreference(….);
Cursor/SimpleCursorAdapter
• Cursor是query返回的游标对象
– 主要Байду номын сангаас法如下:
• • • • • •
moveToFirst/moveToNext/isAfterLast移动游标 getColumnNames得到字段名 getColumnIndex得到字段的索引 getInt/getString/….获取字段值 requery 重新查询 close 关闭游标
android jetpack sharing 用法
Jetpack Sharing 是Android Jetpack 组件库中的一部分,它提供了一组易于使用的API,用于在Android 应用中实现社交分享功能。
通过Jetpack Sharing,您可以轻松地将文本、图片、视频等内容分享到不同的社交媒体平台,如Facebook、Twitter、Instagram 等。
以下是使用Jetpack Sharing 实现Android 应用中的社交分享功能的一般步骤:1. 添加Jetpack Sharing 依赖项首先,您需要在您的Android 项目的`build.gradle` 文件中添加Jetpack Sharing 的依赖项。
在`dependencies` 部分中添加以下代码:```groovyimplementation 'androidx.sharing:sharing-common:1.0.0'implementation 'androidx.sharing:sharing-ui:1.0.0'```2. 配置Sharing Intent接下来,您需要创建一个`SharingIntent` 对象,并配置要分享的内容和目标平台。
您可以使用`ShareCompat.IntentBuilder` 类来构建`SharingIntent`。
例如,以下代码演示了如何分享一张图片到Facebook:```javaBitmap image = ... // 获取要分享的图片Intent sharingIntent = ShareCompat.IntentBuilder.from(this).setType("image/jpeg") // 设置分享文件的MIME 类型.setStream(image) // 设置要分享的图片流.setChooserTitle("分享到Facebook") // 设置选择器标题.build(); // 构建SharingIntent```3. 启动Sharing Intent最后,您可以使用`startActivity` 方法启动`SharingIntent`。
Android开发之 SharedPreferences 使用总结
1.SharedPreferences简介Sharedpreferences是Android平台上一个轻量级的存储类,可以用于保存应用程序的各种配置信息,如应用设置里面的各种开关、是否打开音效、是否使用震动效果、小游戏的玩家积分等,其本质是以“键-值”对的方式保存数据到本地的 xmxxxxl 文件中,其文件保存在/data/data/<package name>/shared_prefs 目录下。
  核心原理:以“键-值”对的方式保存数据到本地的 xmxxxxl 文件中,具体实现是在 SharedPreferencesImpl 里面使用Map来管理,xmxxxxl 文件的具体保存路径是在/data/data/<package name>/shared_prefs 目录下。
SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。
SharedPreferences本身是一个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xmxxxxl文件名,第二个参数具体如下:Context.MODE_APPEND: 追加方式存储Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。
Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序读,写Context.MODE_MULTI_PROCESS: 适用于多进程访问(目前已被废弃,google官方推荐使用ContentProvider来实现进程间共享访问)Editor有如下主要重要方法:SharedPreferences.Editor clear():清空SharedPreferences里所有数据SharedPreferences.Editor putXxx(String key xxx value): 向SharedPreferences存入指定key 对应的数据,其中xxx 可以是booleanfloatint等各种基本类型据SharedPreferences.Editor remove(): 删除SharedPreferences中指定key对应的数据项boolean commit(): 当Editor编辑完成后,使用该方法提交修改首次创建SharedPreferences对象(即SharedPreferences初始化时),会根据文件名将文件下内容一次性加载到mMap容器中,每当我们edit都会创建一个新的EditorImpl对象,当修改或者添加数据时会将数据添加到mModifiled容器中,然后commit或者apply操作比较mMap与mModifiled数据修正mMap中最后一次提交数据然后写入到文件中。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
# 编译为 Java API。 include $(BUILD_DROIDDOC)
c、编写 com.example.android.platform_library.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?> <permissions>
<!-- 库的名称及对应的 Jar 文件位置 -->
具体步骤: a、编写 Java 库,并将源码放到 java 目录下。这一步和编写普通 Java 程序没有差别。 b、编写 Android.mk,内容如下:
# 获得当前目录,清空环境变量 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS)
# 源码所在目录,all-subdir-java-files 表示所有了目录中的 Java 文件。 LOCAL_SRC_FILES := \
该文件还可包括该模块的其它定义,如皮肤等,为了保持该文档清晰易懂的初衷,这里不做介绍,需要了解可以给我邮件。 f、编写 sample/products/sample_addom.mk,内容如下:
# 要植入系统镜像的应用及可选类库。可以包括 Java 库和本地库。这里我们只有 Java 库。 PRODUCT_PACKAGES := \
client 与 upgrade:这是两个依赖该库的应用程序示例。
products:该目录中的文件对包含该库与 Android 框架集成的信息,如模块名称等。 AndroidProducts.mk: 指明该模块的 make 配置文件的在哪里。 sample_addon.mk:模块的配置信息。
sdk_addon:该目录对该库的硬件需求进行定义。 hardware.ini:定义模块对硬件的需求。 manifest.ini:模块的说明文件。名称、供应商等。
* 封装硬件驱动。 * 封装 Java API。 * 移植 C/C++ 代码到 Android 平台。 * 编写原生应用程序。 * 自定义模拟器皮肤。
三、开发者的实践——sample 示例解读。
在源码根目录下有个 vendor (供应商) 目录,专门用于存放各个供应商的会有代码。其中有一个个 sample 目录,这是 Google 用于示范如 何编写自定义共享库的示例,它展示了自定义共享库、JNI 调用、对库的使用方法及皮肤定制等功能。下面我们通过对该示例进行分析,让大家熟 悉这个轻量级的框架。
# 把库的 Jar 包复制到相应的位置。 PRODUCT_SDK_ADDON_COPY_MODULES := \
com.example.android.platform_library:libs/platform_library.jar
# 文档的名称。必须与。 # LOCAL_MODULE:= platform_library # 这个名字配置。 PRODUCT_SDK_ADDON_DOC_MODULE := platform_library
$(call all-subdir-java-files)
# 该模块是可选的。 LOCAL_MODULE_TAGS := optional
# Java 模块名称 LOCAL_MODULE:= com.example.android.platform_library
# 编译为 Java 库。最近以 jar 的形式而不是 apk 的形式存在。 include $(BUILD_JAVA_LIBRARY)
├── java
│ └── com
│ └── example
│
└── android
│
└── platform_library
│
└── PlatformLibrary.java
└── README.txt
Android.mk:该文件说明如何构建该模块。 com.example.android.platform_library.xml:该文件是模块注册时需要的文件。该文件需要被放置到 /system/etc/permissions 目录下。 java/*:Java 源码所在目录。
vendor/
# 这个扩展的名称 PRODUCT_SDK_ADDON_NAME := platform_library
# 把模块的 manifest 和硬件配置文件复制到系统镜像中相应的位置。 PRODUCT_SDK_ADDON_COPY_FILES := \
vendor/sample/sdk_addon/manifest.ini:manifest.ini \ vendor/sample/sdk_addon/hardware.ini:hardware.in
如何添加自己的库又不影响 Android 框架源码呢?如何让要集成的代码与框架耦合度最低,能在各个版本中便捷切换呢? 谁能解救我们?
二、开发者的福音——自定义共享库
由于 Google 自己也在开发第三方库如 Google Map API 等,加上合作伙伴的抱怨,Google 也意识到了这个问题的严重性,于是提供了一 套用于开发自定义共享库的轻量级框架——自定义共享库。通过使用该框架,不但可以添加驱动层到应用层的代码,还有其它一些有趣的功能。该 框架把所有第三方源码整合到一个目录中,可以自由添加删除,便捷的移动,同时也保证了第三方代码的完整性。使用该框架可以完成的任务包括 :
1、首先看一下 sample 目录的结构:
sample ├── Android.mk ├── apps │ ├── Android.mk │ ├── client │ └── upgrade ├── frameworks │ ├── Android.mk │ └── PlatformLibrary ├── MODULE_LICENSE_APACHE2 ├── products │ ├── AndroidProducts.mk │ └── sample_addon.mk ├── README.txt ├── sdk_addon │ ├── hardware.ini │ └── manifest.ini └── skins
# 构建该模块的 Android 平台代号 api=3
# 模块的版本号。必须为整数。 revision=1
# 该模块中包括的共享库列表 libraries=com.example.android.platform_library
# 对每个库的详细定义,格式如下: # <>=<name>.jar;<desc> # 其中 # <>: 通过前面 libraies 定义的库的名称。 # <name>.jar: 包含库 API 的 jar 文件。该文件放在 libs/add-on 下面。 com.example.android.platform_library=platform_library.jar;Sample optional plaform library
# 这个扩展继承系统扩展。 $(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
# 这个扩展的真实名字。这个名字会用于编译。 # 用 'make PRODUCT-<PRODUCT_NAME>-sdk_addon' 的形式来编译此扩展。 PRODUCT_NAME := sample_addon
skins:该目录用于存放自定义皮肤。 WVGAMedDpi:已经定义好的一套皮肤。
2、如何封装 Java 共享库?
Hale Waihona Puke PlatformLibrary 为我们展示了封装 Java 共享库的方法。其目录结构如下:
frameworks/PlatformLibrary
├── Android.mk
├── com.example.android.platform_library.xml
<library name="com.example.android.platform_library" file="/system/framework/com.example.android.platform_library.jar"/>
</permissions>
现在基本的库我们已经编写完成,现在需要对框架中的其它文件进行配置。 d、编写 sample/frameworks/Android.mk, 内容如下:
# 包含子目录中的所有 make 文件 include $(call all-subdir-makefiles)
该文件与 sample/Android.mk 文件相同。 e、编写 sample/sdk_addon/manifest.ini,内容如下:
# 该模块的名称、供应商及描述 name=Sample Add-On vendor=Android Open Source Project description=sample add-on
└── WVGAMedDpi
Android.mk:该文件用于编写构建规则,默认继承 Android 的 make 框架。 frameworks:该目录在这里的意义等同于 Android 源码中的 frameworks 。
PlatformLibrary:该目录就自定义共享库。 apps:该目录用于编写依赖该库的应用程序。经过测试也可以用来编写不依赖该库的程序,这有个好处,让开发商可以把自己特有的应用 集成到框架中。
Android自定义共享库
~~~~~~~~~~~~~~~~~~~~~
作者:曾赛(David Zeng) 邮件:David.Zeng@ 时间:2010-09-12 版本:1.0
一、开发者的难言之隐——讨厌的集成
在 Android 实际开发过程中,每个供应商都会有自己专有的开发库如驱动程序、常用API的封装等。如何把这些用于开发的库无缝地集成到 Android 框架中成为了开发者最为头痛的事,每添加一个新的库就需要把 Android 框架翻个遍,寻找合适的地方放置自己的代码,到最后把 Android 的源码改得支离破碎、面目全非,调试 Bug 或者查看代码得在若大的框架中翻来翻去,一片混乱。如果你以为这是最痛苦的事,那就错 了,最痛苦的事莫过于 Android 平台版本切换,由于 Android 版本更新很快,隔三五几个月,就要把幸苦添加到框架中的代码找出来,重新添加到 新的版本中去,费时费力,令开发人员苦不甚言。但是项目要继续,这些工作必须进行。