Loading... ### 手写代码 1. 列表和adapter - BaseAdapter的使用 ```java package com.zerolouis.practice06.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.*; import com.zerolouis.practice06.R; import com.zerolouis.practice06.bean.Store; import java.util.ArrayList; import java.util.Locale; public class StoreAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener{ private Context mContext; // 声明一个上下文对象 private ArrayList<Store> list; // 商店列表 public StoreAdapter(Context mContext, ArrayList<Store> list) { this.mContext = mContext; this.list = list; } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if(convertView == null){ holder = new ViewHolder(); // 从布局文件item_store_list生成一个新的视图 convertView = LayoutInflater.from(mContext).inflate(R.layout.item_store_list,null); holder.apply_btn = convertView.findViewById(R.id.apply_btn); holder.store_icon = convertView.findViewById(R.id.store_icon); holder.store_name = convertView.findViewById(R.id.store_name); holder.store_new = convertView.findViewById(R.id.store_new); holder.store_number = convertView.findViewById(R.id.store_number); convertView.setTag(holder); // 将视图保存到转换视图中 }else { // 否则从转换视图中取出 holder = (ViewHolder) convertView.getTag(); } Store store = list.get(position); holder.store_name.setText(store.getName()); holder.store_new.setText("新增"); holder.store_number.setText(String.format(Locale.CHINA,"限额%d名",store.getNumber())); holder.store_icon.setImageResource(store.getImage()); holder.store_icon.requestFocus(); String message = "您申请了"+store.getName(); holder.apply_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show(); } }); return convertView; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { return false; } public final class ViewHolder{ private ImageView store_icon; // 商店图标 private TextView store_name; // 商店名称 private TextView store_new; // 是否是新品 private TextView store_number; // 商店的限额 private Button apply_btn; // 申请按钮 } } ``` 列表使用Adapter ```java @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mContext = getActivity(); // 获取活动页面的上下文 mView = inflater.inflate(R.layout.fragment_list,container,false); store_list = mView.findViewById(R.id.store_list); ArrayList<Store> stores = Store.getDefaultList(); // 构造适配器 StoreAdapter storeAdapter = new StoreAdapter(mContext, stores); // 添加适配器 store_list.setAdapter(storeAdapter); // Inflate the layout for this fragment return mView; } ``` 2. 文件的读写,SD卡和共享参数 - **以获取存储卡读写权限为例,简述运行时动态申请权限的步骤** 需要在AndroidManifest.xml中声明相关的权限: ```xml <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> ``` 动态申请权限的步骤如下: 1. 使用`ContextCompat.checkSelfPermission`方法检查是否已经授权了读写权限,如果没有,就调用`ActivityCompat.requestPermissions`方法向用户请求权限,传入一个字符串数组和一个请求码。 2. 在Activity中重写`onRequestPermissionsResult`方法,根据请求码和授权结果进行相应的处理,例如提示用户或者执行读写操作。 3. 在Android 8.0以上的版本中,需要注意申请WRITE权限不会同时获得READ权限,因此要同时申请两个权限。 4. 在Android 10以上的版本中,由于Google修改了`WRITE_MEDIA_STORAGE`权限相关机制,导致了外部SD卡存储不可写的问题。解决办法是适配`DocumentFile`口来操作外置SD卡文件。 ```java // 把字符串保存到指定路径的文本文件 public static void saveText(String path, String txt) { // 根据指定的文件路径构建文件输出流对象 try (FileOutputStream fos = new FileOutputStream(path)) { fos.write(txt.getBytes()); // 把字符串写入文件输出流 } catch (Exception e) { e.printStackTrace(); } } // 从指定路径的文本文件中读取内容字符串 public static String openText(String path) { String readStr = ""; // 根据指定的文件路径构建文件输入流对象 try (FileInputStream fis = new FileInputStream(path)) { byte[] b = new byte[fis.available()]; fis.read(b); // 从文件输入流读取字节数组 readStr = new String(b); // 把字节数组转换为字符串 } catch (Exception e) { e.printStackTrace(); } return readStr; // 返回文本文件中的文本字符串 } // 把指定uri保存为存储卡文件 public static void saveFileFromUri(Context ctx, Uri src, String dest) { try (InputStream is = ctx.getContentResolver().openInputStream(src); OutputStream os = new FileOutputStream(dest);) { int byteCount = 0; byte[] bytes = new byte[8096]; while ((byteCount = is.read(bytes)) != -1){ os.write(bytes, 0, byteCount); } } catch (Exception e) { e.printStackTrace(); } } // 从content://media/external/file/这样的Uri中获取文件路径 public static String getPathFromContentUri(Context context, Uri uri) { String path = uri.toString(); if (path.startsWith("content://")) { String[] proj = new String[]{ // 媒体库的字段名称数组 MediaStore.Video.Media._ID, // 编号 MediaStore.Video.Media.TITLE, // 标题 MediaStore.Video.Media.SIZE, // 文件大小 MediaStore.Video.Media.MIME_TYPE, // 文件类型 MediaStore.Video.Media.DATA // 文件大小 }; try (Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null)) { cursor.moveToFirst(); // 把游标移动到开头 if (cursor.getString(4) != null) { path = cursor.getString(4); } Log.d(TAG, cursor.getLong(0) + " " + cursor.getString(1) + " " + cursor.getLong(2) + " " + cursor.getString(3) + " " + cursor.getString(4)); } catch (Exception e) { e.printStackTrace(); } } return path; } ``` - **共享参数SharedPreferences的使用步骤及关键代码?** 1. 获取`SharedPreferences`对象,可以通过`getSharedPreferences(String, int)`方法指定文件名和访问模式,也可以通过`getPreferences(int)`方法使用默认的文件名和访问模式。 2. 创建`SharedPreferences.Editor`对象,通过调用`edit()`方法。 3. 使用`putString(),putInt()`等方法向`Editor`对象中添加数据。 4. 使用`commit()`或`apply()`方法将数据提交到`SharedPreferences`文件中。 5. 使用`getString()`,`getInt()`等方法从`SharedPreferences`对象中读取数据。 ```java //获取SharedPreferences对象 SharedPreferences sp = getSharedPreferences("data", MODE_PRIVATE); //创建Editor对象 SharedPreferences.Editor editor = sp.edit(); //添加数据 editor.putString("name", "Tom"); editor.putInt("age", 20); editor.putBoolean("married", false); //提交数据 editor.commit(); //读取数据 String name = sp.getString("name", ""); int age = sp.getInt("age", 0); boolean married = sp.getBoolean("married", false); ``` 3. json数据解析 - 使用Android官方提供的org.json包下的类,如JSONObject、JSONArray、JSONStringer等,来解析或构造JSON字符串。这种方式需要手动扣取数据,比较繁琐,但不需要引入第三方库。 - 使用谷歌开源库GSON,来实现JSON字符串与Java对象或集合的相互转换。这种方式可以简化代码,但需要引入GSON库。 - 使用第三方库FastJson,来实现JSON字符串与Java对象或集合的相互转换。这种方式也可以简化代码,但需要引入FastJson库 下面使用jackson 引入依赖 ```java implementation('com.fasterxml.jackson.core:jackson-core:2.11.1') implementation('com.fasterxml.jackson.core:jackson-annotations:2.11.1') implementation('com.fasterxml.jackson.core:jackson-databind:2.11.1') ``` 使用 ```java // 转换对象为json public class Demo { public static void main(String[] args) throws JsonProcessingException { Writer wanger = new Writer("沉默王二", 18); ObjectMapper mapper = new ObjectMapper(); String jsonString = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(wanger); System.out.println(jsonString); } } // 解析成对象 public class TypeReferenceDemo { public static void main(String[] args) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); String json = "[{ \"name\" : \"沉默王三\", \"age\" : 18 }, { \"name\" : \"沉默王二\", \"age\" : 19 }]"; List<Author> listAuthor = mapper.readValue(json, new TypeReference<List<Author>>(){}); System.out.println(listAuthor); } } ``` ### 简答题 1. 描述Android操作系统的架构 > Android操作系统是一个分层的架构,从高层到底层分为四层: > > - 应用程序层:包括手机上的所有APP,无论是系统自带的还是用户开发的。 > - 应用程序框架层:提供了各种Java API,让应用程序可以访问系统服务和硬件功能。 > - 系统运行库和运行环境层:包括了各种C/C++库,如SQLite、OpenGL、WebKit等,以及Android Runtime(ART),负责执行应用程序的字节码。 > - Linux核心层:提供了基本的系统功能,如进程管理、内存管理、设备管理等,并且通过硬件抽象层(HAL)与不同的硬件组件交互。 > 2. 简单介绍Android开发中常用的四大组件 > Android开发中常用的四大组件是: > > - 活动(activity),可视化用户界面。 > - 服务(service),执行持续、耗时且无需用户界面交互的操作。 > - 广播接收器(broadcast receiver),全局监听器,接收来自系统和应用程序的广播。 > - 内容提供器(content provider),共享的持久数据存储机制。 > 3. 请简要描述layout_margin和padding之间的区别 > layout_margin和padding是两个常用的布局属性,它们的区别是: > > - layout_margin用于设置控件与其他控件或父容器之间的距离。 > - padding用于设置控件内部的内容与边框之间的距离。 > 4. 请简要描述layout_gravity和gravity之间的区别。 > layout_gravity和gravity是两个常用的对齐属性,它们的区别是: > > - layout_gravity用于设置控件相对于父容器的位置。 > - gravity用于设置控件内部的内容或子控件的位置。 > 5. 描述Activity的生命周期过程 > Activity的生命周期过程是指一个Activity从创建到销毁的全过程,其中包括以下七个阶段: > > \- onCreate:当Activity第一次被运行时调用此方法,可用于加载布局视图,获取控件命名空间等一些初始化工作。 > \- onStart:当Activity变为可见时调用此方法,此时Activity还没有获取焦点。 > \- onResume:当Activity获取焦点时调用此方法,此时Activity可以与用户进行交互。 > \- onPause:当Activity失去焦点时调用此方法,此时Activity不可见或部分可见。 > \- onStop:当Activity完全不可见时调用此方法,此时Activity可能被销毁或重新启动。 > \- onRestart:当Activity被重新启动的时候,调用此方法。 > \- onDestroy:当Activity被销毁的时候,调用此方法。 > ![img](https://developer.android.google.cn/guide/components/images/activity_lifecycle.png?hl=zh-cn) 6. 简单描述至少3种常用布局? > 安卓中有以下五种常见的布局方式: > > \- 线性布局(LinearLayout):按照垂直或者水平方向布局的组件。 > \- 层布局(FrameLayout):组件从屏幕左上角按照层次堆叠方式布局。 > \- 表格布局(TableLayout):按照行列方式布局组件。 > \- 相对布局(RelativeLayout):相对其它组件的布局方式。 > \- 绝对布局(AbsoluteLayout):按照绝对坐标来定位组件。 > 7. 常见的事件监听方式有哪几种 > - 使用匿名内部类:使用匿名内部类创建事件监听器对象 > - 使用内部类或外部类形式:将事件监听类定义为当前类的内部类或普通的外部类 > - 使用Activity作为事件监听器:通过Activity实现监听器接囗,并实现事件处理方法 > - 绑定标签:在布局文件中为指定标签绑定事件处理方法 > 8. 简述5个以上常见的事件? > 1. Button(按钮)的监听事件:OnClickListener 接口 onClick(View v)接口方法; > 2. SeekBar(进度条)的监听事件:OnSeekBarChangedListener接口, > 3. EditText(编辑器)的监听事件:OnKeyListener接口 > 4. RadioGroup(单选按钮)的监听事件:OnCheckedChangeListener接口 > 5. Spinner(下拉列表)的监听事件:OnItemSelectedListener接口 > 6. DatePicker(日期)日期改变的监听事件: OnDateChangedListener接口 > 7. Dialog(对话框)的监听事件:实现了多个总类型接口,每个总类型接口中有若干个接口,根据不同种类Dialog,会实现不同的接口方法。 > 9. 列举复合按钮的具体类型,常用属性,方法? > 安卓复合按钮(CompoundButton)的具体类型有以下几种: > > - 复选框 **CheckBox** > - 单选按钮 **RadioButton** > - 开关按钮 **Switch** > > 安卓复合按钮的常用属性有以下几种: > > - **android:checked**:设置复合按钮是否被选中 > - **android:button**:设置复合按钮的图标 > - **android:text**:设置复合按钮的文本 > > 安卓复合按钮的常用方法有以下几种: > > - **isChecked()**:返回复合按钮是否被选中 > - **setChecked(boolean checked)**:设置复合按钮是否被选中 > - **setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener listener)**:设置复合按钮的状态改变监听器 > 10. 简述ContentProvider的作用,以及经常需要在子类中重写的方法 > ContentProvider是安卓四大组件之一,它为不同应用之间的数据共享提供了统一的接口。要创建自己的ContentProvider,需要继承抽象类ContentProvider,并重写一些方法: > > - onCreate(): 这个方法在ContentProvider创建时调用。 > - query(): 这个方法用于从指定的uri查询数据,并返回一个Cursor对象。 > - insert(): 这个方法用于向指定的uri的ContentProvider中添加数据。 > - delete(): 这个方法用于从指定的uri的ContentProvider中删除数据。 > - update(): 这个方法用于在指定的uri的ContentProvider中更新数据。 > - getType(): 这个方法用于返回指定的uri中的数据的MIME类型。 > > 这些方法经常在子类中重写,以实现数据访问和操作的具体逻辑。 > 11. 简述基本适配器BaseAdapter的常用方法 > 常用方法: > > - getCount:返回适配器中数据集的数据个数。 > - getItem:根据一个索引(位置)获得该位置的数据项。 > - getItemId:获取条目的id。 > - getView:获取该条目要显示的视图,是最重要的方法。 > 12. 简述在PagerAdapter派生类中需要实现的方法 > 需要实现的方法: > > - getCount:返回适配器中数据集的数据个数,决定了ViewPager的页数。 > - getItem:根据一个索引(位置)获得该位置的页面对象,用于FragmentPagerAdapter和FragmentStatePagerAdapter。 > - instantiateItem:根据一个索引(位置)创建或获取该位置的页面视图,用于PagerAdapter和子类。 > - destroyItem:根据一个索引(位置)销毁或移除该位置的页面视图,用于PagerAdapter和子类。 > - isViewFromObject:判断给定的视图是否和给定的对象关联,用于PagerAdapter和子类。 > - getItemId:获取条目的id,用于FragmentStatePagerAdapter。 > 13. 简述碎片`Fragment`的两种注册方式 > - 静态注册:在XML布局文件中直接放置`fragment`标签,指定碎片类的全名,类似于一个普通控件。这种方式适用于某些通用的页面部件,如Logo条、广告条等,每个活动页面都可以直接引用该部件。 > - 动态注册:在代码中使用`FragmentManager`和`FragmentTransaction`来添加、移除或替换碎片。这种方式适用于需要根据用户操作或程序逻辑来动态改变页面布局的情况,如`ViewPager`、`TabLayout`等,需要配合碎片适配器`FragmentPagerAdapter`或`FragmentStatePagerAdapter`来使用。 > 14. `android`中`intent`与`pendingIntent`的异同? > - `Intent `是立即执行的,而 `PendingIntent `是延迟或挂起的,即它可以在某个时刻或条件下被触发执行。 > - `Intent `是直接由发送方传递给接收方的,而 `PendingIntent `是一个对系统维护的令牌的引用,它可以被发送方传递给其他应用或组件,让它们代表发送方执行预定义的操作。 > - `Intent `是不可变的,即一旦创建就不能修改其内部的数据和属性,而 `PendingIntent `是可变的,即它可以通过 `fillIn()` 方法来更新其内部的 `Intent `的数据和属性。这也带来了一定的安全风险,因为恶意应用可能会修改未填充的 `PendingIntent `字段,从而访问不应该导出的组件。 > - `Intent `只能执行一次,而 `PendingIntent `可以重复执行,除非设置了 FLAG_ONE_SHOT 标志。这也可能导致重放攻击,即恶意应用重复使用相同的 `PendingIntent `来执行本应只能执行一次的操作。 > 15. Notification对象的基本使用流程 > - 创建一个`NotificationManager`对象,用于管理通知的发送和取消。 > - 创建一个`NotificationCompat.Builder`对象,用于设置通知的内容和样式。 > - 调用`Builder`对象的`build()`方法,生成一个`Notification`对象。 > - 调用`NotificationManager`对象的`notify()`方法,传入一个唯一的ID和`Notification`对象,发送通知。 > - 如果需要取消通知,调用`NotificationManager`对象的`cancel()`方法,传入相应的ID。 > 16. 列举`Notification`、`NotificationManager`、`NotificationChannel`的常用方法 > - Notification类:用于创建和配置通知对象,常用的方法有`setSmallIcon()`、`setContentTitle()`、`setContentText()`、`setAutoCancel()`、`setContentIntent()`等。 > - `NotificationManager`类:用于管理通知的发送和取消,常用的方法有`notify()`、`cancel()`、`createNotificationChannel()`、`getNotificationChannel()`等。 > - NotificationChannel类:用于在Android 8.0及以上版本创建和管理通知渠道,常用的方法有`setDescription()`、`setImportance()`、`enableLights()`、`setLightColor()`、`setVibrationPattern()`等。 > 17. Service与生命周期有关的方法 > - Service的生命周期常用的方法有: > - 手动调用的方法:startService(), stopService(), bindService(), unbindService(),它们分别用于启动、关闭、绑定、解绑服务。 > - 自动调用的方法:onCreate(), onStartCommand(), onDestroy(), onBind(), onUnbind(), onRebind(),它们分别用于创建、开始、销毁、绑定、解绑、重新绑定服务。 > 18. 简述Handler相关方法 > Handler的常用方法有: > > - sendMessage(Message msg):发送一个Message对象到消息队列。 > - sendEmptyMessage(int what):发送一个空的Message对象到消息队列。 > - sendEmptyMessageDelayed(int what, long delayMillis):指定延时多少毫秒后发送一个空的Message对象到消息队列。 > - sendMessageDelayed(Message msg, long delayMillis):指定延时多少毫秒后发送一个Message对象到消息队列。 > - post(Runnable r):发送一个Runnable对象到消息队列。 > - postDelayed(Runnable r, long delayMillis):指定延时多少毫秒后发送一个Runnable对象到消息队列。 > - hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息。 > - handleMessage(Message msg):处理消息的方法,通常需要被重写 > 19. 列举一下AsyncTask中经常需要被重写的方法? > **AsyncTask中经常需要被重写的方法**: > > - onPreExecute():在异步任务执行之前,在主线程中调用,通常用于做一些准备工作,如显示进度条**1**[**2**](https://www.jianshu.com/p/817a34a5f200)。 > - doInBackground(Params… params):在异步任务执行之后,在工作线程中调用,通常用于执行耗时的后台操作,如网络请求、文件读写等。 > - onProgressUpdate(Progress… values):在异步任务执行过程中,在主线程中调用,通常用于更新进度信息,如显示进度条的百分比。 > - onPostExecute(Result result):在异步任务执行完毕后,在主线程中调用,通常用于处理后台操作的结果,如更新UI、关闭进度条等。 > 20. Service与IntentService之间的区别 > - Service与IntentService之间的主要区别有: > - Service需要在AndroidManifest.xml中注册,而IntentService不需要。 > - Service需要手动创建工作线程来处理耗时操作,而IntentService内部已经封装了一个工作线程来处理耗时操作。 > - Service需要手动调用stopSelf()或stopService()方法来停止服务,而IntentService会在所有任务执行完毕后自动停止。 > - Service可以处理多个请求,但需要自己管理请求队列,而IntentService可以处理多个请求,并且会以工作队列的方式逐个执行。 > - Service默认运行在主线程中,而IntentService运行在子线程中。 > 最后修改:2023 年 06 月 11 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 2 如果觉得我的文章对你有用,请随意赞赏