简介:调节系统音量的小工具,能够快捷的调节系统铃声,媒体音乐、闹钟和通话声音。你可能会想,手机自带的音量键还不够快捷吗?还得写个程序?首先,用音量键调音只能调节一种声音,像闹钟这种声音不能直接调。其次,我这个小工具支持自动调音。只要事先设定,就能在不同时段把手机音量改成你的计划音量。在安静场合避免因为手机铃声导致的尴尬,休闲的时候不会错过重要的电话。

最终效果图:

第一阶段:获取并修改系统音量

布局:文字(TextView)+进度条(SeekBar)

需要实现的功能是拖动进度条,改变对应的系统音量。使用进度条之前要先设置一个最大值,100就是从0—100,15就是从0—15.我们这里的最值就是系统各音量的最大值。各音量的最大值是不同的。

1、  android系统音量的大小分级。

Android系统的声音分为铃声,媒体音乐,闹钟,通话声音等。每种声音的音量大小分多个等级,默认铃声有15级,通话有7级等。具体可以参考最后的链接。

2、  获取各音频的最大值

获取各音频最值要借助系统AudioManager。从名字上我们就能看出这是系统提供管理声音的Manager.然后调用getStreamMaxVolume(type);传入音频类型。音频的类型可以参考最后的链接。

代码

am = (AudioManager) getSystemService(this.AUDIO_SERVICE);
//各音频流的最大值,参数代表类型
ringMax=am.getStreamMaxVolume(AudioManager.STREAM_RING);
musicMax=am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
alarmMax=am.getStreamMaxVolume(AudioManager.STREAM_ALARM);
callMax=am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);

3、  修改各音频的值

修改同样使用AudioManager

代码

//i是一个整形,要设置的音量大小。
am.setStreamVolume(AudioManager.STREAM_RING,i,0);
am.setStreamVolume(AudioManager.STREAM_MUSIC,i,0);
am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,i,0);
am.setStreamVolume(AudioManager.STREAM_ALARM,i,0);

上面的i从哪里来的?在你拖动进度条的时候,系统会传进来。前提是得告诉系统你需要这个信息。所以我们要为进度条加上观察者。

代码:

ringBar=(SeekBar)findViewById(R.id.main_event_ring);
ringBar.setOnSeekBarChangeListener(barChangeListener); 

观察者代码:

//创建进度条触摸事件观察者
SeekBar.OnSeekBarChangeListener barChangeListener = new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int i, boolean b) {//seekBar.setPressed(b);//seekBar.setProgress(i);  bugif(seekBar==ringBar){am.setStreamVolume(AudioManager.STREAM_RING,i,0);}else if (seekBar==musicBar){am.setStreamVolume(AudioManager.STREAM_MUSIC,i,0);}else if (seekBar==callBar){am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,i,0);}else if (seekBar==alarmBar){am.setStreamVolume(AudioManager.STREAM_ALARM,i,0);}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}
}; 

4、  监听系统声音的改变。

上面我们已经可以通过拖动进度条的方式来设置系统音量。但是当系统音量改变时,进度条不会主动改变,没点反应不行啊。所以在系统音量改变的时候,我们要把进度的值设成当前音量值。

代码:

//获取当前系统音量
void changeBar(){alarmBar.setProgress(am.getStreamVolume(AudioManager.STREAM_ALARM));musicBar.setProgress(am.getStreamVolume(AudioManager.STREAM_MUSIC));ringBar.setProgress(am.getStreamVolume(AudioManager.STREAM_RING));callBar.setProgress(am.getStreamVolume(AudioManager.STREAM_VOICE_CALL));
}

怎么知道系统音量改变了?让系统告诉我们音量改变了。嗯!这是个好方法,所以我们写一个系统音量改变广播的接收器。

代码:

 /*** 处理音量变化时的界面显示*/private class MyVolumeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {//如果音量发生变化则更改seekbar的位置if(intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION"))    {changeBar();}}}

考虑到只有界面出现的时候才需要进度条跟着系统音量一起变化,所以在onStart里面注册,在onStop里面销毁。

注册代码:

//注册音量发生变化时接收的广播
private void myRegisterReceiver(){mVolumeReceiver = new MyVolumeReceiver() ;IntentFilter filter = new IntentFilter() ;filter.addAction("android.media.VOLUME_CHANGED_ACTION") ;registerReceiver(mVolumeReceiver, filter) ;
}

销毁代码:


//销毁监听音量的广播
private void myUnRegisterRecevier(){unregisterReceiver(mVolumeReceiver);
}

第二阶段:添加计划和计划展示功能。

最初的需求是按照计划设定来设置系统的音量。所以在这阶段要实现计划的管理,增删改查。

布局:

1、  一个计划(Event)

上面的布局展示了一个计划有什么。标题,开始时间,结束时间,各音频的设定值。为了处理这些内容,新建一个Event表示一个计划。

字段:静态counter代表Event的总数。每次添加一个Event,counter就加1。Id是每个Event的标识。时间有两个值,String用来显示,int是用来排序的。

Id的设置为counter,之后counter加1

排序:按照开始时间降序排列。计划展示按照时间顺序更方便。

存储:为了便于存储,重写toString()方法。同时提供一个静态方法返回字符串代表的Event.

2、  计划控制器

主要是增删查,改的功能通过删除原来的计划,然后新增一个改动后的计划实现。其他功能是把计划存储到文件里面,从文件里面读出来。

字段:一个静态的Event表,确保所有的操作都在同一份数据上。Context用来获取文件的存取路径。

代码:

package com.example.administrator.soundmanager.controler;import android.content.Context;
import android.os.Environment;
import android.util.Log;import com.example.administrator.soundmanager.model.Event;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class EventControler {private Context mContext;private static List<Event> events=new ArrayList<>();public EventControler(Context mContext) {this.mContext=mContext;if(events.size()<1){getEventFromFile();}}//在表中添加一个事件public boolean addEvent(Event e){events.add(e);saveEvents();return true;}//从表中删除id为eventId的事件。public boolean deleteEvent(int eventId){Iterator<Event> iterator =events.iterator();while (iterator.hasNext()){if(iterator.next().getEventId()==eventId){iterator.remove();}}if (events.size()>0){saveEvents();}else{deleteFile("events.evt");}return true;}//获取id为eventId的事件。public Event getEvent(int eventId){Iterator<Event> iterator =events.iterator();while (iterator.hasNext()){Event e=iterator.next();if(e.getEventId()==eventId){return e;}}return null;}//获取事件记录表public  List<Event> getEvents(){return events;}//将事件记录表中的数据保存到文件中private boolean saveEvents(){if(events.size()>0){StringBuilder stringBuilder=new StringBuilder();for(Event e:events)stringBuilder.append(e+"\n");saveFile(stringBuilder.toString(),"events.evt");return true;}else{return false;}}//从数据文件中读取事件记录。private boolean getEventFromFile(){events.clear();String content=getFile("events.evt");if(content!=null){for(String s: content.split("\n"))events.add(Event.getEvent(s));return true;}return false;}//文件操作。private void saveFile(String str, String fileName) {String cachePath = getCachePath();try {//创建临时文件File tmpFile=new File(cachePath,"temp.evt");// 如果文件存在if (tmpFile.exists()) {// 创建新的空文件tmpFile.delete();}tmpFile.createNewFile();// 获取文件的输出流对象FileOutputStream outStream = new FileOutputStream(tmpFile);// 获取字符串对象的byte数组并写入文件流outStream.write(str.getBytes());// 最后关闭文件输出流outStream.close();// 创建指定路径的文件File file = new File(cachePath, fileName);if(file.exists()){file.delete();}//文件重命名tmpFile.renameTo(file);} catch (Exception e) {e.printStackTrace();Log.e("EventControler","IOException saveFile failed");}}private String getFile(String fileName) {try {// 创建文件File file = new File(getCachePath(),fileName);if(file.exists()){// 创建FileInputStream对象FileInputStream fis = new FileInputStream(file);// 创建字节数组 每次缓冲1Mbyte[] b = new byte[1024];int len = 0;// 一次读取1024字节大小,没有数据后返回-1.// 创建ByteArrayOutputStream对象ByteArrayOutputStream baos = new ByteArrayOutputStream();// 一次读取1024个字节,然后往字符输出流中写读取的字节数while ((len = fis.read(b)) != -1) {baos.write(b, 0, len);}// 将读取的字节总数生成字节数组byte[] data = baos.toByteArray();// 关闭字节输出流baos.close();// 关闭文件输入流fis.close();// 返回字符串对象return new String(data);}else {return null;}} catch (Exception e) {e.printStackTrace();Log.e("EventControler","IOException getFile failed");return null;}}private void deleteFile(String fileName){File file=new File(getCachePath(),fileName);if(file.exists()){file.delete();}}private String getCachePath(){String cachePath ;//外部存储可用if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())|| !Environment.isExternalStorageRemovable()) {cachePath = mContext.getExternalCacheDir().getPath() ;}else{cachePath=mContext.getCacheDir().getPath();}return cachePath;}
}

 

3、  编辑界面

布局在开始的时候展示了。在这个界面需要实现什么呢?新建一个计划和修改一个已有的计划这两个功能。其实修改是把原来的删除之后新建。

因为eventId可以标识一个计划,所以通过eventId的值来判别是修改还是新建。

下面的工作就简单了,只要界面的内容有改动,就把event对应的值进行修改就好。所以,

编辑框添加内容改变观察者

进度条的内容改变观察者跟第一阶段的相似

开始时间和结束时间的获取,使用对话框和TimePicker控件实现。具体参考的链接在最后。

一切都修改完成后,点击完成按钮.如果是修改就先删除原来的值。

 

4、  展示界面

显示界面使用了一个RecycleView控件。这个控件的使用主要是,子项的布局文件,数据源,适配器,布局管理器。

布局

子项布局

数据源:从计划控制器得到

布局管理:竖直方向的线性布局

适配器:直接上代码

 class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder>{private List<Event> events;public ListAdapter(List<Event> eventList) {events=eventList;}@Overridepublic void onBindViewHolder(ViewHolder holder, final int position) {Event e=events.get(position);holder.title.setText(e.getEventName());holder.startTime.setText(e.getStartTime());holder.endTime.setText(e.getEndTime());holder.ring.setProgress(e.getRing());holder.ring.setMax(ringMax);holder.ring.setEnabled(false);//禁止拖动......}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {Context mContext=parent.getContext();final ViewHolder holder=new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_event,parent,false));//点击进入编辑界面View.OnClickListener clickListener=new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent =new Intent(ShowEventsActivity.this,EditEventActivity.class);intent.putExtra("eventId",events.get(holder.getPosition()).getEventId());notifyDataSetChanged();startActivityForResult(intent,1000);}};//长按删除View.OnLongClickListener longClickListener=new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View view) {AlertDialog.Builder builder = new AlertDialog.Builder(ShowEventsActivity.this);builder.setTitle("删除该计划");builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {eventControler.deleteEvent(events.get(holder.getPosition()).getEventId());notifyDataSetChanged();dialogInterface.cancel();}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {dialogInterface.cancel();}});builder.create().show();return true;}};holder.view.setOnClickListener(clickListener);holder.view.setOnLongClickListener(longClickListener);return holder;}@Overridepublic int getItemCount() {return events.size();}class ViewHolder extends RecyclerView.ViewHolder{TextView title;TextView startTime,endTime;SeekBar ring,music,call,alarm;View view;public ViewHolder(View view) {super(view);this.view=view;title=(TextView)view.findViewById(R.id.item_event_title);.....}}}

5、  细节

从展示界面点击list的子项或者点击新建进入编辑界面,编辑完成之后需要更新显示界面。为此在进入编辑界面时采用带返回值的方式启动。这样显示界面就能知道什么时候编辑完成了。

展示界面list的子项在使用进度条展示设定值时,进度条不能被拖动。因此要把进度条设置成禁用状态。

第三阶段:按照设定自动调节系统音量。

第二阶段过后我们就有了计划表,所以自动执行的功能就是这阶段的事了。这里用一个服务完成该功能。服务在运行的时候用当前时间与计划表中的时间作对比,以此判断出当前有效的计划,然后按照计划设定系统音量。

1、  负责调节的服务

后台服务一直运行,每隔一分钟检查一下是否需要改变音量。

代码:

package com.example.administrator.soundmanager;import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.preference.PreferenceManager;import com.example.administrator.soundmanager.controler.EventControler;
import com.example.administrator.soundmanager.model.Event;
import com.example.administrator.soundmanager.util.LOG;import java.util.Calendar;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;public class SoundSetService extends Service {private final String TAG="SoundSetService";private List<Event> eventList;private boolean isRun=false;private int stopCounter=0;private AudioManager am;private NotificationManager notificationManager;//配置文件SharedPreferences preferences;private MyBinder mBinder;//判断是否为服务自己启动private boolean fromMySelf=false;private Handler soundHandler=new Handler(){@Overridepublic void handleMessage(Message msg) {setSysSound();}};public SoundSetService() {}@Overridepublic void onCreate() {super.onCreate();LOG.d(TAG,"..............onCreate");eventList=new EventControler(this).getEvents();//获得配置文件preferences=PreferenceManager.getDefaultSharedPreferences(this);//读取数据,如果无法找到,则使用默认值isRun=preferences.getBoolean("isRun",false);//免打扰权限检测notificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);mBinder=new MyBinder();//使用前台服务保活startForeground(1001,new Notification());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {LOG.d(TAG,"..............onStartCommand");//设置系统音量Message msg=soundHandler.obtainMessage();soundHandler.sendMessage(msg);fromMySelf=intent.getBooleanExtra("fromMySelf",false);//注册定时事件,每过1分钟通过广播自动唤醒服务,使得服务得以长期运行。如果过服务被销毁,则失效final AlarmManager alarmManager=(AlarmManager)getSystemService(Context.ALARM_SERVICE);Intent i=new Intent(this, SoundSetService.class);i.putExtra("fromMySelf",true);final PendingIntent weakUpIntent=PendingIntent.getService(this,0,i,0);alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime()+60000, weakUpIntent);/*** 应用需要加入内存清理白名单*/return START_STICKY;}@Overridepublic IBinder onBind(Intent intent) {LOG.d(TAG,"..............onBind");// TODO: Return the communication channel to the service.return mBinder;}//设置系统音量private void setSysSound(){if(isRun){Event currentEvent=null;//获取当前时间Calendar calendar= Calendar.getInstance();calendar.setTimeInMillis(System.currentTimeMillis());int mHour= calendar.get(Calendar.HOUR_OF_DAY);int mMinute=calendar.get(Calendar.MINUTE);int currentTime=mHour*60+mMinute;//按开始时间降序排列Collections.sort(eventList);//计算当前有效事件。Iterator<Event> eventIterator=eventList.iterator();while (eventIterator.hasNext()){Event e=eventIterator.next();LOG.d(TAG,e.toString()+"sTime:"+e.getsTime()+"eTime:"+e.geteTime());if(e.getsTime()<=currentTime && e.geteTime()>=currentTime){currentEvent=e;break;}}if(currentEvent!=null){setSysSound(currentEvent);LOG.d(TAG,"currentEvent : "+currentEvent.toString());}//服务关闭计数清空if(stopCounter==0){}else{stopCounter=0;}}else if(fromMySelf){//3次试探之后,服务关闭。if(stopCounter>3){stopCounter=0;LOG.d(TAG,"stopSelf()");stopSelf();}else{stopCounter++;}}}private void setSysSound(Event e){if(am==null){am= (AudioManager) getSystemService(this.AUDIO_SERVICE);}if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){if(notificationManager!=null&&notificationManager.isNotificationPolicyAccessGranted()) {am.setStreamVolume(AudioManager.STREAM_RING,e.getRing(),0);am.setStreamVolume(AudioManager.STREAM_SYSTEM,e.getRing(),0);am.setStreamVolume(AudioManager.STREAM_NOTIFICATION,e.getRing(),0);}}else{am.setStreamVolume(AudioManager.STREAM_RING,e.getRing(),0);am.setStreamVolume(AudioManager.STREAM_SYSTEM,e.getRing(),0);am.setStreamVolume(AudioManager.STREAM_NOTIFICATION,e.getRing(),0);}am.setStreamVolume(AudioManager.STREAM_RING,e.getRing(),0);//如果当前有音乐播放,则不改变音量。if(!am.isMusicActive()){am.setStreamVolume(AudioManager.STREAM_MUSIC,e.getMusic(),0);}am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,e.getCall(),0);am.setStreamVolume(AudioManager.STREAM_ALARM,e.getAlarm(),0);}@Overridepublic void onDestroy() {super.onDestroy();LOG.d(TAG,"..............onDestroy");mBinder=null;}public class MyBinder extends Binder{public boolean isRuning(){return isRun;}public void start(){isRun=true;setSysSound();//更新配置文件preferences.edit().putBoolean("isRun",isRun).commit();}public void end(){isRun=false;//更新配置文件preferences.edit().putBoolean("isRun",isRun).commit();}}}

2、  MainActivity设置是否使用自动调节。

在MainActivity中通过绑定的方式,可以控制Service的状态。

在最终效果图可以看到,有个CheckBox,这个用来设置是否使用自动音量调节服务。状态值保留在配置文件里面

CheckBox的观察者。

//设置音量管理服务的运行。void doServiceSet(){if(!serviceBox.isChecked()){if(soundSer==null){}else{if(!soundSer.isRuning()){}else{soundSer.end();}}}else{if(soundSer!=null){if(soundSer.isRuning()){}else{soundSer.start();}}else{bindService(new Intent(MainActivity.this,SoundSetService.class),serviceConnection,0);startService(new Intent(MainActivity.this,SoundSetService.class));serviceBox.setChecked(false);}}}

  

3、  开机自启

这个为了防止开机之后忘记打开软件,造成不好的体验。增加了开机自启。通过静态注册广播接收系统的开机实现。

接收器执行代码

第四阶段、测试

1、sdk>=23的版本上程序闪退

调试报错java.lang.SecurityException: Not allowed to change Do Not Disturb state。查询得知需要动态获取免打扰修改的权限,才能设置系统的音量,媒体、闹钟不需要该权限。

解决:

//获取Do not disturb权限,才可进行音量操作
private void getDoNotDisturb(){NotificationManager nm =(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N& nm!=null &!nm.isNotificationPolicyAccessGranted()) {Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);startActivity(intent);}
}

代码在start后面调用,由用户选择是否授权。

  

源码:https://github.com/Sutg/SoundManager

参考:

文件读写:https://blog.csdn.net/yoryky/article/details/78675373

内置存储和外部存储:https://zm12.sm-tc.cn/?src=l4uLj4zF0NCIiIjRnJGdk5CYjNGckJLQlZaRmJKQz8zOxtCejYuWnJOajNDKysfJysrG0ZeLkpM%3D&uid=7151bc062e1aec6ad263c0f024a4b76e&hid=91d8f653d3b2c3b90d2b247107e56400&pos=1&cid=9&time=1553565744734&from=click&restype=1&pagetype=0020004002000402&bu=ss_doc&query=Android%E7%9A%84%E5%86%85%E7%BD%AE%E5%AD%98%E5%82%A8&mode=&v=1&force=true&wap=false&uc_param_str=dnntnwvepffrgibijbprsvdsdichei

解决RecyclerView item 宽度没有填充屏幕:https://blog.csdn.net/json_corleone/article/details/84230546

广播实现音量同步:https://m.jb51.net/article/101825.htm

音量的获取与设置:https://blog.csdn.net/coderder/article/details/78436892

滚动时间选择器:https://www.cnblogs.com/android-zcq/p/5435681.html

Android系统的音量默认和最大值:https://blog.csdn.net/l0605020112/article/details/35570543

服务保活:https://www.jianshu.com/p/20801232bc7e

转载于:https://www.cnblogs.com/suen061/p/10602617.html

android小工具-系统音量管理器相关推荐

  1. android sdk是灰的,Android Studio 2.3 sdk管理器标签灰显

    Android Studio 2.3 - >配置 - > SDK管理器. 这些选项卡呈灰色显示: SDK工具,SDK更新站点.此外,"Show Package Details&q ...

  2. linux 引导管理器,linux系统引导管理器GRUB

    1.什么是多重操作系统引导管理器及工作原理: 系统启动引导管理器,是在计算机启动后运行的第一个程序,他是用来负责加载.传输控制到操作系统的内核,一旦把内核挂载,系统引导管理器的任务就算完成退出,系统引 ...

  3. linux多系统引导管理,Linux 多重引导MBR与系统引导管理器GRUB.docx

    Linux 多重引导MBR与系统引导管理器GRUB Linux 多重引导MBR与系统引导管理器GRUB1 Linux 多重引导MBR简单的说,从开机到进入操作系统之前的流程应该是这样的:BIOS:开机 ...

  4. python课程小作业之桌面小工具系统

    from tkinter import *#图形界面包 from tkinter import messagebox import os.path import os import math impo ...

  5. 计算机管理器为什么没有本地用户和组,windows7系统服务器管理器没有“本地用户和组”选项解决方法...

    本地用户和组功能可以分配本地用户帐户或组帐户的权限和权利.win7系统点击"计算机"图标右键"管理"来打开"计算机管理"中的"本地 ...

  6. 计算机磁盘无法打开到桌面怎么办,win7系统磁盘管理器被限制无法打开怎么办...

    一般情况下,我们习惯使用自带磁盘管理器操作本地磁盘,方便快捷,只要右键点击计算机"管理"选项即可打开磁盘管理进行操作.使用过程中难免会遇到一些故障问题,最近有用户反馈磁盘管理器无法 ...

  7. win8计算机管理没本地用户和组,win7系统服务器管理器没有本地用户和组选项怎么办?...

    win7系统服务器管理器没有"本地用户和组"选项怎么办? 具体方法如下: 1.出现这种情况一般是组策略做限制了,打开运行gpedit.msc打开组策略编辑器; 2.依次点击,用户设 ...

  8. Win系统服务器管理器打开方式

    Win系统服务器管理器打开方式 1.运行打开服务的命令 services.msc 2.在"计算机管理"中打开服务管理器 按Win+X组合键 或 右键点击Win10开始按钮---&g ...

  9. linux启动grub密码,如何为GRUB系统引导管理器加上密码 | LinuxSir.Org

    作者:北南南北 来自:LinuxSir.Org 摘要:本文主要是讲述就如何为GRUB系统引导管理器加上密码,只要输入密码才能使用GRUB来引导系统:仅限于桌面系统上的应用,不能用 于远程管理的服务器上 ...

最新文章

  1. 印象笔记终于支持Markdown了 !你还会再用其他笔记吗?
  2. 工作 10 年,月薪过万者不足三成,程序员却笑了!
  3. Facebook究竟是怎么抄袭Snapchat的?听听他们自己怎么说
  4. DELL R730 iDRAC8远程控制卡使用
  5. 《An Introduction to Ray Tracing》——2.2 Ray/Sphere Intersection And Mapping
  6. java t800,T800KPI绩效考核系统C#源码
  7. 京东万能转链API接口 含商品信息优惠券转链 京东线报如何转链?
  8. Excel可视化:1个思路学会7种变形柱形图
  9. stm32 火灾自动报警及联动控制源码_火灾自动报警系统和消防联动系统的区别
  10. Python教程:利用百度API进行批量图片OCR识别
  11. 做视频类网站如何选择云服务器?
  12. Centos如何设置ip黑名单,防止被攻击
  13. 十年后再读《轻公司》
  14. ios 各种动画机制
  15. CSDN的markdown的KaTex数学公式
  16. 手机端html跑马灯效果,使用css3和js在手机上实现简单的跑马灯效果
  17. 电脑最全快捷键--建议收藏
  18. 阿里云数据库优惠活动: 数据库MySQL配置价格表出炉
  19. MQL5 细则手册:在 MQL5 中开发多交易品种波动指标
  20. windows10 critical_service_failed解决办法

热门文章

  1. jquery confirm使用
  2. Minikube介绍及安装
  3. 电商总结(一)小型电商网站的架构
  4. 警惕 iPhone 6即将上市 诈骗邮件已现身网络
  5. 高并发多队列网卡设置CPU亲和性项目记录
  6. 进程管理工具——Supervisord简介及安装
  7. 架构整洁之道-书中箴言
  8. DQN、蒙特卡洛树搜索(MCTS)
  9. 开休闲食品店怎么样?
  10. python figure函数_python3_matplotlib_figure()函数解析