#java #android
#java #Android
Вопрос:
Пожалуйста, не помечайте его как дубликат, я пытался следовать ответам на другие вопросы, но это не помогло.
У меня проблема с приложением, которое я создал в intellij в соответствии с руководством, предоставленным моим профессором (да, я попросил о помощи, он не смог мне помочь). Я сделал все в соответствии с руководством (или я так думаю), но когда я пытаюсь запустить приложение, я получаю это сообщение в моем Logcat:
2020-11-30 17:50:05.577 9262-9262/com.example.todoapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.todoapp, PID: 9262
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.example.todoapp.Task.getName()' on a null object reference
at com.example.todoapp.TaskFragment.onCreateView(TaskFragment.java:54)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663)
at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435)
at android.app.Activity.performStart(Activity.java:8024)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Из этого сообщения я понимаю, что существует проблема с Task().getName, но я не знаю, почему оно должно быть нулевым. Вот мои действия: MainActivity.java
package com.example.todoapp;
import androidx.fragment.app.Fragment;
import android.os.Bundle;
import java.util.UUID;
public class MainActivity extends SingleFragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected Fragment createFragment() {
UUID taskId = (UUID) getIntent().getSerializableExtra(TaskListFragment.KEY_EXTRA_TASK_ID);
return TaskFragment.newInstance(taskId);
}
}
SingleFragmentActivity.java:
package com.example.todoapp;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public abstract class SingleFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
if(fragment == null) {
fragment = createFragment();
fragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
protected abstract Fragment createFragment();
}
TaskListActivity.java:
package com.example.todoapp;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
public class TaskListActivity extends SingleFragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public Fragment createFragment() {
return new TaskListFragment();
}
}
And here are my classes: Task.java
package com.example.todoapp;
import java.util.Date;
import java.util.UUID;
public class Task {
private UUID id;
private String name;
private Date date;
private boolean done;
public Task() {
id = UUID.randomUUID();
date = new Date();
}
public UUID getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
}
TaskFragment.java:
package com.example.todoapp;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import java.util.UUID;
public class TaskFragment extends Fragment {
private EditText nameField;
private Button dateButton;
private CheckBox doneCheckBox;
private Task task;
private static final String ARG_TASK_ID = "ARG_TASK_ID";
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID taskId = (UUID) getArguments().getSerializable(ARG_TASK_ID);
task = TaskStorage.getInstance().getTaskById(taskId);
}
public static TaskFragment newInstance(UUID taskId) {
Bundle bundle = new Bundle();
bundle.putSerializable(ARG_TASK_ID, taskId);
TaskFragment taskFragment = new TaskFragment();
taskFragment.setArguments(bundle);
return taskFragment;
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater,container,savedInstanceState);
View view = inflater.inflate(R.layout.fragment_task, container, false);
nameField = view.findViewById(R.id.task_name);
dateButton = view.findViewById(R.id.task_date);
doneCheckBox = view.findViewById(R.id.task_done);
nameField.setText(task.getName());
nameField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
task.setName(charSequence.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
dateButton.setText(task.getDate().toString());
dateButton.setEnabled(false);
doneCheckBox.setChecked(task.isDone());
doneCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
task.setDone(isChecked);
}
});
return view;
}
}
TaskLIstFragment.java:
package com.example.todoapp;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class TaskListFragment extends Fragment {
public static final String KEY_EXTRA_TASK_ID = "KEY_EXTRA_TASK_ID";
private TaskAdapter adapter;
private RecyclerView recyclerView;
private void updateView() {
TaskStorage taskStorage = TaskStorage.getInstance();
List<Task> tasks = taskStorage.getTaskList();
if (adapter == null) {
adapter = new TaskAdapter(tasks);
recyclerView.setAdapter(adapter);
} else {
adapter.notifyDataSetChanged();
}
}
@Override
public void onResume() {
super.onResume();
updateView();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_task_list, container, false);
recyclerView = view.findViewById(R.id.task_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateView();
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private class TaskHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView nameTextView;
private TextView dateTextView;
private Task task;
private final LinearLayout itemElement = itemView.findViewById(R.id.item_element);
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.putExtra(KEY_EXTRA_TASK_ID, task.getId());
startActivity(intent);
}
public void bind(final Task task) {
this.task = task;
itemElement.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.putExtra(KEY_EXTRA_TASK_ID, task.getId());
startActivity(intent);
}
});
nameTextView.setText(task.getName());
dateTextView.setText(task.getDate().toString());
}
public TaskHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.list_item_task, parent, false));
itemView.setOnClickListener(this);
nameTextView = itemView.findViewById(R.id.task_item_name);
dateTextView = itemView.findViewById(R.id.task_item_date);
}
}
private class TaskAdapter extends RecyclerView.Adapter<TaskHolder> {
private List<Task> tasks;
public TaskAdapter(List<Task> tasks) {
this.tasks = tasks;
}
@NonNull
@Override
public TaskHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
return new TaskHolder(layoutInflater, parent);
}
@Override
public void onBindViewHolder(@NonNull TaskHolder holder, int position) {
Task task = tasks.get(position);
holder.bind(task);
}
@Override
public int getItemCount() {
return tasks.size();
}
}
}
TaskStorage.java:
package com.example.todoapp;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TaskStorage {
private static final TaskStorage ourInstance = new TaskStorage();
public static TaskStorage getInstance() {
return ourInstance;
}
private List<Task> taskList;
private TaskStorage() {
taskList = new ArrayList<Task>();
for(int i=0; i<100; i ) {
Task tmp = new Task();
tmp.setName("Task nr " i);
taskList.add(tmp);
}
}
public List<Task> getTaskList() {
return taskList;
}
public Task getTaskById(UUID id) {
for(Task task:taskList) {
if(task.getId().equals(id)) return task;
}
return null;
}
}
Я думаю, что проблема связана с UUID. Он выдает null, я не знаю почему. Я был бы очень признателен за помощь, поскольку я не знаю, что делать. Любая помощь будет оценена.
Я попытался изменить
@Override
public void afterTextChanged(Editable s) {
}
Для:
@Override
public void afterTextChanged(Editable s) {
nameField.setText(task.getName());
}
Я также попытался инициализировать name в конструкторе задачи, но ошибка все еще была там.
Комментарии:
1. «… существует проблема с Task().getName, но я не знаю, почему оно должно быть нулевым». Это неправильный вопрос. 🙂 Ваша задача — сделать его не нулевым, прежде чем использовать его. Итак, правильный вопрос: «Почему это не должно быть null?». Единственная причина, которую я вижу в приведенном коде, это
task = TaskStorage.getInstance().getTaskById(taskId);
. Так что же это за загадкаTaskStorage
и что заставляет нас предполагать, что существуют какие-либо задачи с даннымtaskId
? Похоже, единственное место в коде, которое может помещать в него какие-либо задачи, — это конструктор :private TaskStorage() {...}
. Я бы сосредоточился на этом. Вызывается ли оно?2. Я не внимательно следил за кодом, но каждой задаче присваивается случайный идентификатор. Указан ли правильный идентификатор при попытке найти задачу? Я бы тоже сосредоточился на этом.
Ответ №1:
В вашем TaskFragment
:
Либо метод onCreate
не вызывался ранее onCreateView
(поэтому переменная экземпляра task
никогда не назначалась), либо TaskStorage.getInstance().getTaskById(taskId);
просто возвращает null, потому что для данного идентификатора нет задачи — и вы активно возвращаете null в таких случаях.
Я не могу судить о вызове методов жизненного цикла, поскольку я не занимаюсь разработкой приложений, но я думаю, что это, вероятно, то, что знает ваш профессор.
Поэтому я думаю, что для данного идентификатора просто нет задачи.
Вы можете избежать NullPointerException
, вернув фиктивную задачу, например:
public Task getTaskById(UUID id) {
for(Task task:taskList) {
if(task.getId().equals(id)) return task;
}
return new Task();
}
Но это, скорее всего, не решит вашу проблему, просто переместив ее в другое место.
Я думаю, что более чистым способом было бы либо создать исключение, когда задача не может быть найдена, либо вернуть необязательный параметр и обработать его должным образом:
public Optional<Task> getTaskById(UUID id) {
for(Task task:taskList) {
if(task.getId().equals(id)) return Optional.of(task);
}
return Optional.empty();
}
/// or:
public Task getTaskById(UUID id) {
for(Task task:taskList) {
if(task.getId().equals(id)) return task;
}
throw new IllegalArgumentException(String.format("There is no task for id '%s'", id));
}
Я бы лично предпочел необязательный вариант, поскольку для вызывающих методы ясно, что существует вероятность возврата пустого необязательного значения.